Skip to content

Instantly share code, notes, and snippets.

@Eclmist
Last active September 4, 2019 16:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Eclmist/493bba4382710fb20b3d9f80bc8380f3 to your computer and use it in GitHub Desktop.
Save Eclmist/493bba4382710fb20b3d9f80bc8380f3 to your computer and use it in GitHub Desktop.
Simple raytracing in Source Academy
// 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