Public
Edited
Jan 1, 2023
Insert cell
Insert cell
render();
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
aspectRatio = 16 / 9;
Insert cell
height = width / aspectRatio;
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function ray(origin, direction) {
const at = t => vec3.lerp(vec3.create(), origin, direction, t);
at.origin = origin;
at.direction = direction;
return at;
}
Insert cell
function rayColor(ray, world) {
const hit = {};

if (world(ray, 0, Infinity, hit)) {
return vec3.scale(vec3.create(), vec3.add(vec3.create(), hit.normal, vec3.fromValues(1,1,1)), 0.5);
}
const unitDirection = unit(ray.direction);
const t = 0.5 * (unitDirection[1] + 1);
const white = vec3.fromValues(1, 1, 1);
const blue = vec3.fromValues(0.5, 0.7, 1.0);
const blend = vec3.lerp(vec3.create(), white, blue, t);
return blend;
}
Insert cell
function getRay(u, v) {
const a = vec3.scaleAndAdd(vec3.create(), lowerLeftCorner, horizontal, u);
const b = vec3.scaleAndAdd(vec3.create(), a, vertical, v);
return ray(origin, vec3.sub(vec3.create(), b, origin));
}
Insert cell
Insert cell
function sphere(center, radius) {
return function(ray, tMin, tMax, hit) {
const oc = vec3.sub(vec3.create(), ray.origin, center);
const a = vec3.squaredLength(ray.direction);
const halfB = vec3.dot(oc, ray.direction);
const c = vec3.squaredLength(oc) - radius*radius;
const discriminant = halfB*halfB - a*c;
if (discriminant < 0) return false;
const sqrtD = Math.sqrt(discriminant);
let root = (-halfB - sqrtD) / a;
if (root < tMin || tMax < root) {
root = (-halfB + sqrtD) / a;
if (root < tMin || tMax < root) return false;
}
hit.t = root;
hit.p = ray(hit.t);
const outwardNormal = vec3.scale(vec3.create(), vec3.subtract(vec3.create(), hit.p, center), 1 / radius);
hit.frontFace = vec3.dot(ray.direction, outwardNormal) < 0;
hit.normal = hit.frontFace ? outwardNormal : -outwardNormal;
return true;
}
}
Insert cell
function hitSphere(center, radius, r) {
const oc = vec3.sub(vec3.create(), r.origin, center);
const a = vec3.squaredLength(r.direction);
const halfB = vec3.dot(oc, r.direction);
const c = vec3.squaredLength(oc) - radius*radius;
const discriminant = halfB*halfB - a*c;
if (discriminant < 0) { return -1.0; }
else { return (-halfB - Math.sqrt(discriminant) ) / a; }
}
Insert cell
function hittableList(items) {
return function(ray, tMin, tMax, hit) {
const tempHit = {};
let hitAnything = false;
let closestSoFar = tMax;
items.forEach(item => {
if (item(ray, tMin, closestSoFar, tempHit)) {
hitAnything = true;
closestSoFar = tempHit.t;
Object.assign(hit, tempHit);
}
});
return hitAnything;
}
}
Insert cell
Insert cell
world = {
const items = [
sphere(vec3.fromValues(0, 0, -1), 0.5),
sphere(vec3.fromValues(-0.9, -0.3, -0.8), 0.2),
sphere(vec3.fromValues(0, -100.5, -1), 100)
];
return hittableList(items);
}
Insert cell
Insert cell
function render() {
const context = DOM.context2d(width, height, 1);
const image = context.getImageData(0, 0, width, height);
const data = image.data;
const a = vec3.create();
let i = 0;
for (let j = height-1; j >= 0; j--) {
for (let k = 0; k < width; k++) {
const pixelColor = vec3.create();
for (let s = 0; s < samplesPerPixel; s++) {
const u = (k + Math.random()) / (width - 1);
const v = (j + Math.random()) / (height - 1);
const r = getRay(u, v);
vec3.add(pixelColor, pixelColor, rayColor(r, world));
}
vec3.round(pixelColor, vec3.scale(pixelColor, pixelColor, 255 / samplesPerPixel));
data[i++] = pixelColor[0]; // red
data[i++] = pixelColor[1]; // green
data[i++] = pixelColor[2]; // blue
data[i++] = 255; // alpha
}
}
context.putImageData(image, 0, 0);
return context.canvas;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
testColor = {
const world = sphere(vec3.fromValues(0, 0, -1), 0.5);
const r = ray(
origin,
vec3.sub(vec3.create(),
vec3.scaleAndAdd(vec3.create(),
vec3.scaleAndAdd(vec3.create(), lowerLeftCorner, horizontal, testX),
vertical, testY),
origin)
);
return rayColor(r, world);
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more