Task 6 of 6

Soft Shadows, Full Scene

The payoff. A point is in shadow when something sits between it and the light — and you already own the tool that answers that: march again, from the hit point toward the light. Here's Inigo Quilez's beautiful upgrade: instead of a binary blocked/clear, track how closely the shadow ray grazes the scene. The running minimum of 3 · d / t (distance over travel) is ≈1 when the ray stays clear, 0 when it's blocked, and slides smoothly between when it grazes — a free penumbra.

The kernel takes a shadowOn switch so you can A/B it: with the light swung low to the left, the left lobe casts a soft-edged shadow across the neck of the blob. One more march, and a scene with no triangles anywhere gets real cinematography.

The driver also hands you a separation slider, which is task 2's point made in one gesture: the program is a pure function of its controls, so dragging it re-runs the whole thing. Slide the two centres together and smin welds them into a single blob; pull them apart and the neck thins, necks down, and snaps. Solve the task first — it defaults to the 0.55 the scene has always used — then drag it.

Goal: add the shadow march — 32 steps from the hit point toward the light, penumbra factor sh = Math.min(sh, 3.0 * d / st) — and scale the diffuse term by it when shadowOn is 1.

Requirements

Hint 1 — why 3 · d / t?

At travel distance st, a field value d means the ray passes within d of an occluder. The ratio d / st is the sine of the "clearance angle" from the surface point — small angle, deep penumbra. The 3 just sets how sharp the shadow edge is.

Hint 2 — the loop, spelled out
let sh = 1.0;
let st = 0.06;
for (let j = 0; j < 32; j++) {
  const d = sceneDist(wx + lx * st, wy + ly * st,
    pz + lz * st, sep, r, k);
  sh = Math.min(sh, 3.0 * d / st);
  st += Math.max(d, 0.02);
}
sh = Math.max(sh, 0.0);

And remember to only apply it when shadowOn > 0.5.

Same idea elsewhere

Secondary rays are the moment ray-marching meets real-time ray tracing: shadow rays are exactly what RTX/DXR hardware accelerates, and this penumbra estimate ships in countless WGSL and GLSL engines as the cheap alternative when you can't afford one.

All tasks in Ray-Marched Metaballs

  1. The Sphere as a Number
  2. Two Spheres Melt Into One
  3. March Until You Hit Something
  4. Normals Without Geometry
  5. Turn On the Light
  6. Soft Shadows, Full Scene

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.