Skip to content

P0-C1: SDF Sphere Marching

Taught: 2026-04-24 (during Phase 0 Bootstrap) Milestone: Phase 0 — Bootstrap Result: PASS (after walkthrough with concrete numerical trace; original abstract teaching was insufficient) Backfilled: 2026-05-04 — original prose was scattered across the early planning session; reconstructed into the corrected conversational format

Why this concept matters here

Phase 0's core deliverable is a single sphere rendered in the browser via SDF ray-marching. Every later concept (composition, central-difference normals, Disney BRDF, MarchViz) builds on the march loop. Without the right intuition for "ray starts at the camera, steps by the SDF value, hits when distance is tiny, misses when t blows up," nothing else in the renderer makes sense.

The walkthrough

Step 1: What an SDF actually returns

An SDF (Signed Distance Function) is a function f(p) that, given any 3D point p, returns the distance to the nearest surface, with a sign:

  • Positive if p is outside the surface
  • Negative if p is inside
  • Zero if p is exactly on the surface

For a sphere of radius r at origin: f(p) = length(p) - r. Pick some points to verify:

Point plength(p)f(p) = length(p) - 1Meaning
(0, 0, 0) (origin, inside)0−1"1 unit inside the surface"
(0.5, 0, 0)0.5−0.5"0.5 inside"
(1, 0, 0) (on surface)10"at the surface"
(2, 0, 0) (outside)2+1"nearest surface 1 away"
(5, 0, 0) (far away)5+4"nearest surface 4 away"

Crucially, the SDF tells you the distance to the nearest surface. So at any point in space, you know how far you can move in any direction without hitting anything. That's what makes ray marching work.

Step 2: The march loop — what t means

We want to render the surface from a camera. We shoot a ray from the camera position into the scene, in some direction (computed from screen pixel + camera FOV).

ray_origin = camera_position
ray_dir    = normalize(point_we're_aiming_at - camera_position)

Now we walk along that ray. The variable t is how far we've walked from the camera. At t = 0 we're standing at the camera. At t = 5 we're 5 units along the ray.

current_position = ray_origin + ray_dir * t

We start with t = 0 (at the camera) and step forward.

Step 3: How big a step to take

Naive approach: take fixed-size steps, e.g., t += 0.01. Problem: at 0.01 per step, reaching a surface 5 units away takes 500 iterations. Too slow.

Smart approach (sphere tracing): at every position, ask the SDF "how far to the nearest surface?" Then step that far. The SDF guarantees nothing is closer, so this step is safe — you can't overshoot.

loop:
  d = scene_sdf(current_position)
  if d < HIT_EPS: HIT, stop
  t += d
  if t > MAX_DIST: MISS, stop

This is the entire algorithm. Each iteration:

  • Sample the SDF
  • If we're basically on the surface (d < 0.001), declare a hit
  • Otherwise, take a step of size d (the safe step)
  • If we've walked too far without hitting anything (t > 20 or so), declare a miss

Step 4: Concrete numerical trace — sphere at origin, radius 1, camera at (0, 0, 3)

Let's actually trace a ray through the algorithm, with real numbers, to see the safe-step shrinking pattern.

Setup: sphere of radius 1 at origin. Camera at (0, 0, 3) looking toward origin. Ray direction: (0, 0, -1). So current_position = (0, 0, 3 - t).

Itertpositionlength(p)d = length(p)−1action
00.0(0, 0, 3.0)3.02.0step by 2
12.0(0, 0, 1.0)1.00.0hit boundary; step by 0 (or hit at this iter if eps tolerates)

Wait — at iteration 1, d is exactly 0 (we landed perfectly on the surface). With HIT_EPS = 0.001, we'd register a hit. In practice, ray directions aren't aligned to surfaces this cleanly, so let's redo with a slightly off-axis ray:

Ray from (0, 0, 3) toward (0.3, 0, 0). Direction (0.3, 0, -3) / length ≈ (0.0995, 0, -0.995).

Itertpositionlength(p)daction
00.0(0, 0, 3.0)3.02.0step by 2
12.0(0.199, 0, 1.01)1.0300.030step by 0.030
22.030(0.202, 0, 0.980)1.0010.001HIT (d < HIT_EPS)

Three iterations. Notice the safe steps shrink as you approach the surface — first step was 2.0 (big), then 0.030, then converged. This is the algorithm's whole magic: when you're far from anything, you take huge leaps; when you're near a surface, you crawl.

For a ray that misses the sphere entirely (e.g., aimed at (2, 0, 0)):

Itertpositionlength(p)daction
00.0(0, 0, 3.0)3.02.0step by 2
12.0(1.11, 0, 1.34)1.740.74step by 0.74
22.74(1.52, 0, 0.73)1.690.69step by 0.69
33.43(1.91, 0, 0.16)1.910.91step by 0.91
......ray flies past sphere, getting farthergrows
~25>20far awayhugehugeMISS (t > MAX_DIST)

For miss rays, t eventually exceeds the bailout threshold (MAX_DIST = 20 or 50).

Step 5: Why the magic numbers

Three constants in every march loop:

  • HIT_EPS = 0.001 — how close is "on the surface enough." Smaller values = more accurate hit point but more iterations and more numerical noise. 0.001 is the standard for unit-scale scenes.
  • MAX_STEPS = 64 — iteration cap. If we haven't hit OR exceeded MAX_DIST in 64 steps, give up. Prevents infinite loops in pathological cases (e.g., a ray grazing parallel to a surface, taking tiny steps forever).
  • MAX_DIST = 20.0 (or 50): "if t exceeds this, the ray is into the void, declare a miss." Sets the effective render distance.

For unit-scale scenes (sphere radius ~1, camera ~3 units back), these defaults work. If you scale your scene up 100×, scale these too.

Step 6: Why the algorithm is correct (the safety argument)

The whole algorithm rests on one invariant: at every position, the SDF tells you a SAFE step distance — guaranteed not to overshoot.

Why? Because SDF returns "distance to nearest surface." If the nearest surface is 0.5 units away, you can move 0.5 units in any direction without hitting anything. Moving along the ray is "any direction," so it's safe.

This is what makes sphere tracing different from naive ray marching with fixed steps — it adaptively scales step size by what the geometry tells you. Far from surfaces: huge leaps. Near surfaces: tiny crawl. Every step is provably safe.

The mental model in one sentence

A ray marches from the camera by repeatedly asking the SDF "how far to the nearest surface?" and stepping that far — converging fast in empty space and slow near surfaces, terminating with a hit when distance is sub-epsilon and a miss when total travel exceeds a bailout threshold.

Explain-back question

Walk me through what happens in the march loop for two specific rays in a scene with a unit sphere at origin, camera at (0, 0, 3):

  1. A ray that hits the sphere — where is t after 1 step? After 2? When does d < 0.001 trigger?
  2. A ray that misses — what does t do over many iterations, and which exit condition fires?

Use the actual numerical values, not abstract reasoning.

User's answer (PASS)

When we trace a ray from camera first t=0... Then a ray is shot at the direction of the sphere. You calculate d... This time it is on the sphere and <0.001 check for floats pass... If it returns -1, the ray completely missed all the primitives in the scene!

Judgment

PASS. Got the core mechanics:

  • t=0 starts at camera ✓
  • Ray traced toward the sphere ✓
  • d shrinks as we approach ✓
  • HIT_EPS check fires when on surface ✓
  • Miss case identified (referenced returning -1, the conventional "no hit" sentinel) ✓

Slight mix-up: the HIT condition is d < HIT_EPS (small d), and miss is t > MAX_DIST (large t). The "-1" in his answer is a function-return convention from some implementations, not a value of d. Minor; the mental model is intact. Numerical trace would have been crisper but the algorithmic understanding is there.

Conversational walkthroughs of real-time graphics + Rust/WASM concepts.