Published
Edited
Jul 5, 2018
8 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function simpleRaymarch (uv, scene) {
// uv: is the pixel position of the canvas (normalized in 0 to 1)
// scene: is a function that give the closest object distance for a given point in space
// We will initialize a ray using the canvas coordinate and pointing along Z axis. we normalize it. the third value "1" can be modified to have a different "camera zoom". (perspective projection, not orthgraphic)
const direction = normalize([ 2 * uv[0] - 1, 2 * uv[1] - 1, 1 ]);
// Now we'll "raymarch", throw the current ray using the direction, accumlate the distance to the closest object and stop if reached.
let totalDist = 0.0;
const ITERATIONS = 40; // how much iteration for our algorithm? increase for better quality, decrease for better performance
for (let i = 0; i < ITERATIONS; i++) {
// p is the 3D position of our "photon" along the ray direction
const p = mul(direction, totalDist);
// we use that 3D position to calculate the distance with closest object in space.
const dist = scene(p);
// in our case, we accumulate that distance to progress our photon for the next iteration
totalDist += dist;
}
// Now, we know exactly when our ray hits an object in space.
// We're simply going to use the the distance to reach the object to color the pixel:
// I call this the fog factor because it's how much you lose "light" with the distance". Our object is just going to emit a RED color!
const fogFactor = smoothstep(10, 50, totalDist);
return mix([ 1, 0, 0 ], [ 0.1, 0.1, 0.1 ], fogFactor).concat(1.0);
}
Insert cell
Insert cell
function sdSphere(p, radius) {
return length(p) - radius;
}
Insert cell
Insert cell
Insert cell
Insert cell
canvas(uv => simpleRaymarch(uv, p => {
p = add(p, [ 0, 0, -2 ]) // we move our photon 2z behind, which means we move our sphere 2z above!
return sdSphere(p, 0.8)
}))
Insert cell
Insert cell
function sdTorus(p, t) {
const q = [ length([ p[0], p[2] ]) - t[0], p[1] ];
return length(q) - t[1];
}
Insert cell
canvas(uv => simpleRaymarch(uv, ([x, y, z]) => {
return sdTorus([ x, y, z - 2 ], [ 0.8, 0.3 ])
}))
Insert cell
Insert cell
canvas(uv => simpleRaymarch(uv, ([x, y, z]) => {
return sdTorus([ x, z - 2, y ], [ 0.8, 0.3 ])
}))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function finalScene (p) {
// We are offsetting out camera. actually think of it more that we are offseting our "photon".
//p = add(p, [ 5, 5.8, -0.2 ]);
// Place the origin
p = add(p, [0, -1, -2.2]);
// rotate with an angle
const rotY = 0.3;
p[1] = Math.cos(rotY) * p[1] + Math.sin(rotY) * p[2];
p[2] = -Math.sin(rotY) * p[1] + Math.cos(rotY) * p[2];

// Offseting a bit the position will create a cool blob effect. This is cheating a bit with the system because it makes the distance function wrong so we have to be careful about this usage
p[0] += 0.02 * Math.cos(8 * p[1] + 3 * p[2]);
p[1] += 0.02 * Math.sin(6 * p[2] + 7 * p[0]);
p[2] += 0.02 * Math.cos(8 * p[0] + 9 * p[0]);
// We are repeating the space! again this is cheating again, but basically this allow use to repeat objects for free!
p = opRep(p, [ 10, 14, 4 ]);
// Now, we're applying our sphere distance equation
const sphere = sdSphere(p, 0.7);
p = add(p, [0, 0.8, 0]);
const cylinder = sdCappedCylinder(p, [0.4, 0.5]);
const shape = smin(sphere, cylinder, 0.5);
// And we can return this distance!
return shape
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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