Public
Edited
Jan 29, 2024
4 stars
Insert cell
Insert cell
{
// Create the context
const context = DOM.context2d(ewidth, height);
context.canvas.value = context;

// Just set a color based upon our (i,j) value. Note, in Canvas space, the origin
// is *upper left*, not *lower left*. I don't correct for this.
// Fill in a pattern based upon pixel position in image coordinates
for (let i = 0; i < ewidth; ++i) {
for (let j = 0; j < height; ++j) {
// filStyle uses CSS color names; so you need a *string* rgb function.
context.fillStyle = `rgb(${Math.floor((i / ewidth) * 255)}, ${Math.floor(
(j / height) * 255
)}, 0)`;
context.fillRect(i, j, 1, 1);
}
}

return context.canvas;
}
Insert cell
Insert cell
Insert cell
{
// Create the context
const context = DOM.context2d(ewidth, height);
context.canvas.value = context;

// Flip the context vertically
context.transform(1, 0, 0, -1, 0, height);

// Fill in a pattern based upon pixel position. Due to the flip, image and spatial
// coordinates align
for (let i = 0; i < ewidth; ++i) {
for (let j = 0; j < height; ++j) {
// Just set a color based upon our (i,j) value. Note, in Canvas space, the origin
// filStyle uses CSS color names; so you need a *string* rgb function.
context.fillStyle = `rgb(${Math.floor((i / ewidth) * 255)}, ${Math.floor(
(j / height) * 255
)}, 0)`;
context.fillRect(i, j, 1, 1);
}
}

return context.canvas;
}
Insert cell
Insert cell
Insert cell
glm = import("gl-matrix")
Insert cell
Insert cell
eye = glm.vec3.fromValues(0, 0, 1000)
Insert cell
Insert cell
view_dir = glm.vec3.fromValues(0, 0, -1)
Insert cell
Insert cell
viewport_offset = (0.5 * height) / Math.tan(glm.glMatrix.toRadian(60 / 2))
Insert cell
Insert cell
Insert cell
Insert cell
up = glm.vec3.fromValues(0, 1, 0)
Insert cell
Insert cell
ihat = glm.vec3.cross(
glm.vec3.create(),
up,
glm.vec3.negate(glm.vec3.create(), view_dir)
)
Insert cell
jhat = up
Insert cell
Insert cell
p_00 = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.scaleAndAdd(
glm.vec3.create(),
viewport_center,
ihat,
-0.5 * ewidth + 0.5
),
jhat,
-0.5 * height + 0.5
)
Insert cell
Insert cell
{
// Create the context
const context = DOM.context2d(ewidth, height);
context.canvas.value = context;

// Flip the context vertically
context.transform(1, 0, 0, -1, 0, height);

// Fill in a pattern based upon the ray's direction. Redder means more to the right,
// green more to to top, blue means farther away
let ray_origin = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.scaleAndAdd(glm.vec3.create(), p_00, ihat, 600),
jhat,
0
);

// Where is the ray going? We use perpective, so its ray_origin - eye
let ray_dir = glm.vec3.normalize(
glm.vec3.create(),
glm.vec3.subtract(glm.vec3.create(), ray_origin, eye)
);

for (let i = 0; i < ewidth; ++i) {
for (let j = 0; j < height; ++j) {
// What is the ray origin?
let ray_origin = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.scaleAndAdd(glm.vec3.create(), p_00, ihat, i),
jhat,
j
);

// Where is the ray going? We use perpective, so its ray_origin - eye
let ray_dir = glm.vec3.normalize(
glm.vec3.create(),
glm.vec3.subtract(glm.vec3.create(), ray_origin, eye)
);

// filStyle uses CSS color names; so you need a *string* rgb function.
context.fillStyle = `rgb(${Math.floor(
0.5 * (ray_dir[0] + 1) * 255
)}, ${Math.floor(0.5 * (ray_dir[1] + 1) * 255)}, ${Math.floor(
0.5 * (ray_dir[2] + 1) * 255
)})`;
context.fillRect(i, j, 1, 1);
}
}

return context.canvas;
}
Insert cell
Insert cell
Insert cell
s1_origin = glm.vec3.fromValues(0, 0, 0)
Insert cell
s1_radius = 0.25 * ewidth
Insert cell
Insert cell
function intersect_sphere(origin, dir, center, radius) {
// Check discriminant
const a = 1.0;
const origin_sub_center = glm.vec3.subtract(
glm.vec3.create(),
origin,
center
);
const b = glm.vec3.dot(dir, origin_sub_center);
const c =
glm.vec3.dot(origin_sub_center, origin_sub_center) - radius * radius;
if (b * b - a * c < 0) return Number.NEGATIVE_INFINITY;

// Find roots
let t0 = (-b - Math.sqrt(b * b - a * c)) / a;
let t1 = (-b + Math.sqrt(b * b - a * c)) / a;
if (t0 < 0) return t1;
else return t0;
}
Insert cell
Insert cell
{
// Create the context
const context = DOM.context2d(ewidth, height);
context.canvas.value = context;

// Flip the context vertically
context.transform(1, 0, 0, -1, 0, height);

const origin = glm.vec3.fromValues(0, 0, 0);

// Color based upon if the sphere was hit-
for (let i = 0; i < ewidth; ++i) {
for (let j = 0; j < height; ++j) {
// What is the ray origin?
let ray_origin = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.scaleAndAdd(glm.vec3.create(), p_00, ihat, i),
jhat,
j
);

// Where is the ray going? We use perpective, so its ray_origin - eye
let ray_dir = glm.vec3.normalize(
glm.vec3.create(),
glm.vec3.subtract(glm.vec3.create(), ray_origin, eye)
);

// Did we hit the sphere?
if (intersect_sphere(ray_origin, ray_dir, s1_origin, s1_radius) > 0)
context.fillStyle = "white";
else context.fillStyle = "black";

context.fillRect(i, j, 1, 1);
}
}

return context.canvas;
}
Insert cell
Insert cell
s2_origin = glm.vec3.fromValues(-100, +100, 175)
Insert cell
s2_radius = 0.15 * ewidth
Insert cell
Insert cell
{
// Create the context
const context = DOM.context2d(ewidth, height);
context.canvas.value = context;

// Flip the context vertically
context.transform(1, 0, 0, -1, 0, height);

const s1_origin = glm.vec3.fromValues(0, 0, 0);
const s1_radius = 0.25 * ewidth;
const s2_origin = glm.vec3.fromValues(-100, +100, 175);
const s2_radius = 0.15 * ewidth;

// Color based upon which is closest and if there are multiple intersections
for (let i = 0; i < ewidth; ++i) {
for (let j = 0; j < height; ++j) {
// What is the ray origin?
let ray_origin = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.scaleAndAdd(glm.vec3.create(), p_00, ihat, i),
jhat,
j
);

// Where is the ray going? We use perpective, so its ray_origin - eye
let ray_dir = glm.vec3.normalize(
glm.vec3.create(),
glm.vec3.subtract(glm.vec3.create(), ray_origin, eye)
);

// Did we hit the first sphere?
let s1_dist = intersect_sphere(ray_origin, ray_dir, s1_origin, s1_radius);

// Did we hit the second sphere?
let s2_dist = intersect_sphere(ray_origin, ray_dir, s2_origin, s2_radius);

// Color appropriately
if (s1_dist >= 0 && s2_dist < 0) context.fillStyle = "red";
else if (s1_dist < 0 && s2_dist >= 0) context.fillStyle = "green";
else if (s1_dist > 0 && s2_dist >= 0 && s2_dist < s1_dist)
context.fillStyle = "blue";
else if (s1_dist >= 0 && s2_dist >= 0 && s2_dist >= s1_dist)
context.fillStyle = "yellow";
else context.fillStyle = "black";

context.fillRect(i, j, 1, 1);
}
}

return context.canvas;
}
Insert cell
Insert cell
light_dir = glm.vec3.normalize(glm.vec3.create(), glm.vec3.fromValues(-1, 1, 3))
Insert cell
Insert cell
function shade_phong(
normal,
to_light,
to_view,
diffuse_color,
specular_color,
shininess
) {
// Ambient color
const color = glm.vec3.scale(glm.vec3.create(), diffuse_color, 0.1);

const ndotl = glm.vec3.dot(normal, to_light);
if (ndotl > 0) {
glm.vec3.scaleAndAdd(color, color, diffuse_color, ndotl);

const reflect = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.negate(glm.vec3.create(), light_dir),
normal,
2 * ndotl
);
const rdotv = glm.vec3.dot(reflect, to_view);
if (rdotv > 0)
glm.vec3.scaleAndAdd(
color,
color,
specular_color,
Math.pow(rdotv, shininess)
);
}

return color;
}
Insert cell
Insert cell
s1_diffuse = glm.vec3.fromValues(0.5, 0.5, 0.5)
Insert cell
s1_specular = glm.vec3.fromValues(0.4, 0.4, 0.4)
Insert cell
s1_shininess = 64
Insert cell
s2_diffuse = glm.vec3.fromValues(0.7, 0, 0)
Insert cell
s2_specular = glm.vec3.fromValues(0.2, 0.2, 0.2)
Insert cell
s2_shininess = 2
Insert cell
Insert cell
objects = [
{
origin: s1_origin,
radius: s1_radius,
diffuse: s1_diffuse,
specular: s1_specular,
shininess: s1_shininess,
intersect: function (r, d) {
return intersect_sphere(r, d, this.origin, this.radius);
}
},
{
origin: s2_origin,
radius: s2_radius,
diffuse: s2_diffuse,
specular: s2_specular,
shininess: s2_shininess,
intersect: function (r, d) {
return intersect_sphere(r, d, this.origin, this.radius);
}
}
]
Insert cell
{
// Create the context
const context = DOM.context2d(ewidth, height);
context.canvas.value = context;

// Flip the context vertically
context.transform(1, 0, 0, -1, 0, height);

// Phong shade the spheres locally
for (let i = 0; i < ewidth; ++i) {
for (let j = 0; j < height; ++j) {
// What is the ray origin?
let ray_origin = glm.vec3.scaleAndAdd(
glm.vec3.create(),
glm.vec3.scaleAndAdd(glm.vec3.create(), p_00, ihat, i),
jhat,
j
);

// Where is the ray going? We use perpective, so its ray_origin - eye
let ray_dir = glm.vec3.normalize(
glm.vec3.create(),
glm.vec3.subtract(glm.vec3.create(), ray_origin, eye)
);

// Test each object individually. Note that the intersection function
// has been made part of the object for generality: You can define your
// own object that has an intersect method
let hit = undefined;
let hit_dist = Number.POSITIVE_INFINITY;
for (let i = 0; i < objects.length; ++i) {
let dist = objects[i].intersect(ray_origin, ray_dir);
if (dist >= 0 && (hit === undefined || dist < hit_dist)) {
hit = i;
hit_dist = dist;
}
}

// Color appropriately
if (hit === undefined) context.fillStyle = "black";
else {
let point = glm.vec3.scaleAndAdd(
glm.vec3.create(),
ray_origin,
ray_dir,
hit_dist
);
let object = objects[hit];
let color = shade_phong(
glm.vec3.normalize(
glm.vec3.create(),
glm.vec3.subtract(glm.vec3.create(), point, object.origin)
),
light_dir,
glm.vec3.negate(glm.vec3.create(), view_dir),
object.diffuse,
object.specular,
object.shininess
);
context.fillStyle = `rgb(${255 * color[0]}, ${255 * color[1]}, ${
255 * color[2]
})`;
}

context.fillRect(i, j, 1, 1);
}
}

return context.canvas;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more