// ray_tracer.cm — Simple ray tracer kernel // Control flow + numeric + allocation. Classic VM benchmark. var math = use('math/radians') function vec(x, y, z) { return {x: x, y: y, z: z} } function vadd(a, b) { return {x: a.x + b.x, y: a.y + b.y, z: a.z + b.z} } function vsub(a, b) { return {x: a.x - b.x, y: a.y - b.y, z: a.z - b.z} } function vmul(v, s) { return {x: v.x * s, y: v.y * s, z: v.z * s} } function vdot(a, b) { return a.x * b.x + a.y * b.y + a.z * b.z } function vnorm(v) { var len = math.sqrt(vdot(v, v)) if (len == 0) return vec(0, 0, 0) return vmul(v, 1 / len) } function make_sphere(center, radius, color) { return { center: center, radius: radius, color: color } } function intersect_sphere(origin, dir, sphere) { var oc = vsub(origin, sphere.center) var b = vdot(oc, dir) var c = vdot(oc, oc) - sphere.radius * sphere.radius var disc = b * b - c if (disc < 0) return -1 var sq = math.sqrt(disc) var t1 = -b - sq var t2 = -b + sq if (t1 > 0.001) return t1 if (t2 > 0.001) return t2 return -1 } function make_scene() { var spheres = [ make_sphere(vec(0, -1, 5), 1, vec(1, 0, 0)), make_sphere(vec(2, 0, 6), 1, vec(0, 1, 0)), make_sphere(vec(-2, 0, 4), 1, vec(0, 0, 1)), make_sphere(vec(0, 1, 4.5), 0.5, vec(1, 1, 0)), make_sphere(vec(1, -0.5, 3), 0.3, vec(1, 0, 1)), make_sphere(vec(0, -101, 5), 100, vec(0.5, 0.5, 0.5)) ] var light = vnorm(vec(1, 1, -1)) return {spheres: spheres, light: light} } function trace(origin, dir, scene) { var closest_t = 999999 var closest_sphere = null var i = 0 var t = 0 for (i = 0; i < length(scene.spheres); i++) { t = intersect_sphere(origin, dir, scene.spheres[i]) if (t > 0 && t < closest_t) { closest_t = t closest_sphere = scene.spheres[i] } } if (!closest_sphere) return vec(0.2, 0.3, 0.5) // sky color var hit = vadd(origin, vmul(dir, closest_t)) var normal = vnorm(vsub(hit, closest_sphere.center)) var diffuse = vdot(normal, scene.light) if (diffuse < 0) diffuse = 0 // Shadow check var shadow_origin = vadd(hit, vmul(normal, 0.001)) var in_shadow = false for (i = 0; i < length(scene.spheres); i++) { if (scene.spheres[i] != closest_sphere) { t = intersect_sphere(shadow_origin, scene.light, scene.spheres[i]) if (t > 0) { in_shadow = true break } } } var ambient = 0.15 var intensity = in_shadow ? ambient : ambient + diffuse * 0.85 return vmul(closest_sphere.color, intensity) } function render(width, height, scene) { var aspect = width / height var fov = 1.0 var total_r = 0 var total_g = 0 var total_b = 0 var y = 0 var x = 0 var u = 0 var v = 0 var dir = null var color = null var origin = vec(0, 0, 0) for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { u = (2 * (x + 0.5) / width - 1) * aspect * fov v = (1 - 2 * (y + 0.5) / height) * fov dir = vnorm(vec(u, v, 1)) color = trace(origin, dir, scene) total_r += color.x total_g += color.y total_b += color.z } } return {r: total_r, g: total_g, b: total_b} } var scene = make_scene() return { raytrace_32x32: function(n) { var i = 0 var result = null for (i = 0; i < n; i++) { result = render(32, 32, scene) } return result }, raytrace_64x64: function(n) { var i = 0 var result = null for (i = 0; i < n; i++) { result = render(64, 64, scene) } return result } }