Skip to content

Instantly share code, notes, and snippets.

@ebobby
Last active March 2, 2019 02:52
Show Gist options
  • Select an option

  • Save ebobby/17e9314cd330258ecc7c9eaf6b3f0b18 to your computer and use it in GitHub Desktop.

Select an option

Save ebobby/17e9314cd330258ecc7c9eaf6b3f0b18 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>Ray Tracing</title>
</head>
<body>
<div style="width:1280px; margin:0 auto;">
<canvas id="img" width="1280" height="720" style="border:1px solid #000000;">
</canvas>
</body>
<script language="JavaScript" src="raytracer.js"></script>
</html>
Canvas = (function(){
var canvas = document.getElementById("img"),
width = canvas.width, height = canvas.height,
ctx = canvas.getContext("2d");
return {
width, height,
clear: function () {
ctx.clearRect(0, 0, width, height);
},
put_pixel: function(x, y, color) {
var gamma = 1 / 2.2;
var r = Math.pow(color.x, gamma) * 255, g = Math.pow(color.y, gamma) * 255, b = Math.pow(color.z, gamma) * 255;
ctx.fillStyle = "rgb(" + r + ", " + g + ", " + b +")";
ctx.fillRect(x, y, 1, 1);
}
};
})();
Raytracer = (function() {
var width = Canvas.width,
height = Canvas.height;
var half_height = height / 2,
half_width = width / 2;
function cast_ray(scene, ray) {
var t = Number.POSITIVE_INFINITY,
point, normal, color;
for (var i = 0; i < scene.objects.length; i++) {
var distance = scene.objects[i].intersect(ray);
if (distance < t) {
t = distance;
point = ray.o.add(ray.d.mul_float(t));
normal = scene.objects[i].normal(point);
color = scene.objects[i].color;
}
}
if (t < Number.POSITIVE_INFINITY)
return { point, normal, color };
else
return false;
}
function color(intersection, scene) {
var lighting = new Vec3(0, 0, 0);
for (var i = 0; i < scene.lights.length; i++) {
var nudged = intersection.point.add(intersection.normal),
light_dir = scene.lights[i].pos.sub(intersection.point).normalize(),
light_int = scene.lights[i].intensity,
light_col = scene.lights[i].color;
if (!cast_ray(scene, { o: nudged, d: light_dir })) {
var light_angle = Math.max(light_dir.dot(intersection.normal), 0.0);
lighting = lighting.add(light_col.mul_float(light_angle * light_int));
}
}
return intersection.color.mul_vec(lighting);
}
return {
render: function(scene) {
var img_x = 0,
img_y = 0,
bg = new Vec3(0.1, 0.1, 0.1),
intersection, c;
for (var world_y = half_height; world_y > -half_height; world_y--) {
for (var world_x = -half_width; world_x < half_width; world_x++) {
var ray = {
o: new Vec3(world_x, world_y, 0),
d: new Vec3(0, 0, -1.0),
};
if (intersection = cast_ray(scene, ray))
c = color(intersection, scene);
else
c = bg;
Canvas.put_pixel(img_x, img_y, c);
img_x++;
}
img_x = 0; img_y++;
}
}
};
})();
function Scene() {
this.objects = [];
this.lights = [];
}
Scene.prototype.add_object = function(object) {
this.objects.push(object);
};
Scene.prototype.add_light = function(light) {
this.lights.push(light);
};
function Light(pos, intensity, color) {
this.pos = pos;
this.intensity = intensity;
this.color = color;
}
function Sphere(center, radius, color) {
this.center = center;
this.radius = radius;
this.color = color;
}
Sphere.prototype.intersect = function(ray) {
var oc = ray.o.sub(this.center),
a = ray.d.dot(ray.d),
b = 2.0 * oc.dot(ray.d),
c = oc.dot(oc) - this.radius * this.radius;
var discriminant = b * b - 4.0 * a * c,
dis_sqrt = Math.sqrt(discriminant);
if (discriminant >= 0.0) {
var t0 = (-b - dis_sqrt) / (2.0 * a),
t1 = (-b + dis_sqrt) / (2.0 * a);
if (t0 < Number.POSITIVE_INFINITY && t0 > Number.EPSILON)
return t0;
if (t1 < Number.POSITIVE_INFINITY && t1 > Number.EPSILON)
return t1;
}
return Number.POSITIVE_INFINITY;
};
Sphere.prototype.normal = function(p) {
return p.sub(this.center).normalize();
};
function Vec3(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vec3.prototype.length = function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
};
Vec3.prototype.normalize = function() {
var len = this.length();
return new Vec3(this.x / len, this.y / len, this.z / len);
};
Vec3.prototype.dot = function(other) {
return this.x * other.x + this.y * other.y + this.z * other.z;
};
Vec3.prototype.add = function(other) {
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
};
Vec3.prototype.sub = function(other) {
return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z);
};
Vec3.prototype.mul_float = function(factor) {
return new Vec3(this.x * factor, this.y * factor, this.z * factor);
};
Vec3.prototype.mul_vec = function(other) {
return new Vec3(this.x * other.x, this.y * other.y, this.z * other.z);
};
s = new Scene();
s.add_object(new Sphere(new Vec3(0, 0, -200), 95, new Vec3(0.5, 0.5, 0.2)));
s.add_object(new Sphere(new Vec3(-200, 0, -200), 95, new Vec3(0.5, 0.2, 0.2)));
s.add_object(new Sphere(new Vec3(200, 0, -200), 95, new Vec3(0.2, 0.2, 0.5)));
s.add_light(new Light(new Vec3(1000, 00, 0), 0.5, new Vec3(1, 0, 0)));
s.add_light(new Light(new Vec3(-1000, 00, 0), 0.5, new Vec3(0, 0, 1)));
s.add_light(new Light(new Vec3(0, 1000, 0), 1.5, new Vec3(1, 1, 1)));
Raytracer.render(s);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment