Last active
September 4, 2019 16:31
-
-
Save Eclmist/493bba4382710fb20b3d9f80bc8380f3 to your computer and use it in GitHub Desktop.
Simple raytracing in Source Academy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// IMPORTANT: increase the code execution time limit up to 20000 milliseconds. This may take some time to render. | |
function raytracer() { | |
// Resolution | |
const res = parse_int(prompt("Choose resolution (recommended: 200)"), 10); | |
const sample = parse_int(prompt("Anti-aliasing (MSAA) number of samples (1 - 8)"), 10); | |
const roughness = parse_int(prompt("Surface roughness (0 - 100)"), 10) / 100; | |
const col = [0.5, 0.5, 0.6]; // r, g, b | |
const skycol = [0.5, 0.7, 1.0]; | |
function dot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } | |
function vAdd(a, b) { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; } | |
function vAddC(a, c) { return [a[0] + c, a[1] + c, a[2] + c]; } | |
function vMul(a, b) { return [a[0] * b[0], a[1] * b[1], a[2] * b[2]]; } | |
function vMulC(a, c) { return [a[0] * c, a[1] * c, a[2] * c]; } | |
function vInv(a) { return [-a[0], -a[1], -a[2]]; } | |
function sphere(center, radius, rO, rD, tMin, tMax) | |
{ | |
const oc = vAdd(rO, vInv(center)); | |
const a = dot(rD, rD); | |
const b = dot(oc, rD); | |
const c = dot(oc, oc) - (radius * radius); | |
const discrim = b * b - a * c; | |
// bHit, t, point, normal | |
if (discrim > 0) | |
{ | |
let t = (-b - math_sqrt(b*b-a*c)) / a; | |
if (t < tMax && t > tMin) | |
{ | |
const p = vAdd(rO, vMulC(rD, t)); | |
const n = vMulC(vAdd(p, vInv(center)), 1/radius); | |
return [true, t, p ,n]; | |
} | |
else | |
{ | |
return [false]; | |
} | |
} | |
else | |
{ | |
return [false]; | |
} | |
} | |
function sky(rD) | |
{ | |
const t = 0.5 * (rD[1] + 1); | |
return vAddC(skycol, (1 - t) * 0.3); | |
} | |
function getSceneHit(ro, rd, tMin, tMax) | |
{ | |
const h1 = sphere([0, 0.2, 0], 0.2, ro, rd, tMin, tMax); | |
const h2 = sphere([0, -5, 0], 5, ro, rd, tMin, h1[0] ? h1[1] : tMax); | |
return h2[0] ? h2 : h1; | |
} | |
function getSceneHitCheap(ro, rd, tMin, tMax) | |
{ | |
return sphere([0, -5, 0], 5, ro, rd, tMin, tMax); | |
} | |
function getColor(hit, depth, acc) | |
{ | |
if (depth <= 0) | |
{ | |
return acc; | |
} | |
else | |
{ | |
const reflO = hit[2]; | |
const reflD = vAdd(hit[3], vMulC([math_random() * 2 - 1, math_random() * 2 - 1, math_random() * 2 - 1], roughness)); | |
// return reflD; | |
const refHit = getSceneHit(reflO, reflD, 0.001, 1000); | |
return refHit[0] | |
? getColor(refHit, depth - 1, vMul(acc, col)) | |
: vMul(acc, sky(reflD)); | |
} | |
} | |
// Similar to a fragment function in GLSL | |
function frag(u, v) { | |
// return color(square, u, v, 0); | |
let c = [0, 0, 0]; | |
for (let i = 0; i < sample; i = i + 1) | |
{ | |
// Create camera ray | |
const ro = [0, 3, -10]; | |
const rd = vMulC([u - 0.5 + math_random() * 0.005, v - 3.3 + math_random() * 0.005, 10], 2); | |
const ray = [ro, rd]; | |
// hack to avoid double object intersection test on edge of screen | |
const hit = u <= 0.25 || u > 0.75 || v < 0.25 || v > 0.75 | |
? getSceneHitCheap(ro, rd, 0.001, 1000) | |
: getSceneHit(ro,rd, 0.001, 1000); | |
if (hit[0]) | |
{ | |
//c = vAddC(vMulC(hit[3], 1/2), 0.5); | |
c = vAdd(c, getColor(hit, 3, [0.5, 0.5, 0.5])); | |
} | |
else | |
{ | |
const rd2 = vMulC([u - 0.5 + math_random() * 0.005, v - 0.5 + math_random() * 0.005, 10], 2); | |
c = vAdd(c, sky(rd2)); | |
} | |
} | |
return color(square, c[0] / sample, c[1] / sample, c[2] / sample); | |
} | |
// Generates row of square, colored by the frag function | |
function helper_x(x, y, acc) { | |
// we call frag(x / rex, y / res) to stack a new colored black_bb to our accumulator. | |
// remember to divide by res to normalize our x and y values to between 0-1 | |
return x >= res / 2 | |
? acc | |
: helper_x(x + 1, y, beside_frac(x / (x + 1), acc, frag(x / res, 1 - y / res))); | |
} | |
// Generate column of rows | |
function helper_y(y, acc) { | |
const x = helper_x(0, y, blank); | |
const xx = beside(x, flip_horiz(x)); | |
return y >= res | |
? acc | |
: helper_y(y + 1, stack_frac(y / (y + 1), acc, xx)); | |
} | |
return helper_y(0, blank); | |
} | |
show(raytracer()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment