← All Labs
IDEA #29 · GEOMETRY OF LIGHT — RECURSIVE RENDERING

Raytracing: light as a tree of bounces

A photorealistic image looks like magic, but it is really just geometry solved backwards. Instead of following light from the lamp — most of which never reaches you — a raytracer shoots one ray out of your eye through each pixel and asks a single question: "what is the first thing I hit, and where did its colour come from?" When the surface is a mirror or glass, the answer is another ray — so every pixel unfolds into a little tree of reflections and refractions. Below, the whole scene is raytraced live on your GPU, one ray per pixel per bounce; the second panel slows it right down and draws the individual rays so you can watch the tree grow.

01

Drive it — a scene rendered pixel-by-pixel

Every pixel you see is one ray fired from the camera, bouncing up to the depth you set. Orbit the camera, swing the light, and turn the middle sphere into a mirror or into glass — watch reflections and refractions appear as extra bounces switch on.
DRIVE IT
rays / frame · max bounce depth3 on GPU ✓
The image is computed by solving ray–sphere and ray–plane intersections for every pixel, every frame. Turn the bounce depth to 0 and the mirror goes black and the glass goes opaque — with no follow-up rays there is nothing to reflect or refract. Each extra bounce is one more level of the tree.
CAMERA ORBIT−25°
CAMERA HEIGHT1.6
LIGHT DIRECTION35°
LIGHT HEIGHT2.6
MAX BOUNCE DEPTH3
MIDDLE SPHERE MATERIAL
GLASS INDEX n1.50
SPHERES
3
+ ground plane
RESOLUTION
pixels traced
FPS
live
02

Ray debug — draw the individual bounces

Same scene, seen edge-on and slowed to a crawl. One primary ray leaves the eye; at each surface it can spawn a reflection (mirror) and a refraction (glass), and every hit also fires a shadow ray toward the light. Slide the pixel across the film and cap the depth to watch the tree branch and prune.
RAY DEBUG
primary / camera ray reflection refraction shadow ray (lit) shadow ray (blocked)
PIXEL ACROSS FILM50%
PIXEL HEIGHT46%
DEBUG BOUNCE DEPTH4
RAYS DRAWN
this pixel
TREE DEPTH
bounces used
LEAVES
rays that escaped
A single camera ray can explode into dozens of secondary rays. Cap the depth and the deepest branches are simply cut off — cheaper, but reflections-of-reflections vanish. That trade-off is the central knob of every renderer.
03

Walkthrough

Five moves to feel where the picture comes from.
WALKTHROUGH
1
In Drive it, drag Max bounce depth down to 0. The mirror sphere turns flat black and the glass sphere turns opaque grey: with no secondary rays, a reflective surface has nothing to show and a transparent one has nothing to see through.
2
Now raise depth 1 → 2 → 3. At depth 1 the mirror shows the room but its own reflected mirrors are still black; at depth 2 those fill in; reflections-of-reflections keep appearing. Each step is one more level of the tree.
3
Swing the Light direction and watch the shadows sweep. Every shaded point fires a hidden shadow ray at the lamp — if something blocks it, the point is dark. Shadows are just "did my ray reach the light?"
4
Switch the middle sphere to Glass and slide the index n from 1.0 to 2.5. The background bends and flips behind it — that is Snell's law refracting the ray twice, once entering and once leaving.
5
Open Ray debug and drag the pixel onto the mirror, then onto the glass. Count the branches in the readout, then lower the debug depth and watch the deep branches get pruned away — exactly what the depth cap does to the full render.
04

Aha

The one line to leave with.
AHA
THE INSIGHT
A photorealistic image is not painted — it is solved. Fire one ray backwards out of the eye, and every time it meets a shiny or clear surface it asks the same question again of a new ray. Rendering is that question, recursively: "what light arrives here, and where did it come from?"
05

Explanation

Why tracing light backwards works, and what each bounce is really computing.
EXPLANATION

Backwards is the whole trick. Real photons stream out of a lamp in every direction, and only a vanishingly small fraction ever land on your retina. Simulating them forwards would waste almost all its effort on light you never see. Because the physics of light is reversible, we can run it the other way: shoot rays from the eye, through the pixels of the image plane, and out into the scene. Each ray asks only about light that actually matters — the light that reaches this pixel.

A pixel is an intersection test. For every pixel, we solve where its ray first meets a surface. A ray is a point plus a direction, p(t) = o + t·d; a sphere is all points a fixed distance from its centre. Substituting one into the other gives a quadratic in t — its smallest positive root is the nearest hit. A plane is even simpler, one linear equation. The winner is the closest positive t across all objects.

Colour is local light plus borrowed light. At the hit point we compute direct shading: how much the lamp illuminates it, dimmed by the angle between the surface and the light (Lambert's cosine law) and switched off entirely if a shadow ray to the lamp is blocked. Then, if the surface is a mirror or glass, we borrow: we spawn a new ray and repeat the entire procedure to find out what that reflected or refracted direction sees.

Reflection and refraction are two rules. A mirror sends the ray off symmetrically about the surface normal — the reflection law r = d − 2(d·n)n. Glass bends the ray as it crosses the boundary according to Snell's law, n₁ sin θ₁ = n₂ sin θ₂, refracting once on the way in and again on the way out. Turn the index up and the bending gets stronger; past a critical angle the ray can't leave at all and reflects internally instead.

Recursion builds a tree. Because each shiny hit spawns another ray, a single camera ray becomes a branching tree: primary → reflection and refraction → their reflections and refractions, and so on. Left unchecked a glass-and-mirror scene would trace forever, so we impose a maximum depth. That single number trades realism for speed: shallow trees render fast but miss reflections-of-reflections; deep trees look richer but cost more. The Ray debug panel is literally drawing this tree, one branch per bounce.

From two spheres to Hollywood. The same recursive question — scaled to millions of triangles, textured materials, and area lights, and sampled with many random rays per pixel (path tracing) — is what renders modern films and, increasingly, real-time games on ray-tracing GPUs. The engine here is a toy, but the idea is exactly the production one.

06

Research note

The geometry each bounce evaluates.
RESEARCH NOTE
Ray–sphere intersection (a quadratic in t)
Reflection direction about the normal n
Snell's law of refraction
The rendering equation — what every ray is estimating

Outgoing light at a point is emitted light plus all incoming light reflected by the surface's BRDF fr — an integral the raytracer samples one ray at a time. Recursion is how the Li term (incoming light) is evaluated: it's just Lo at wherever that ray lands.

Go deeper: Turner Whitted, "An Improved Illumination Model for Shaded Display" (1980) — the paper that introduced recursive ray tracing. James Kajiya, "The Rendering Equation" (1986). Peter Shirley, Ray Tracing in One Weekend (free online) — build the whole thing from scratch. Éric Veach's thesis on path tracing for the statistics of sampling light.

Idea #29 of 45 · single self-contained HTML · WebGL fragment-shader raytracer (Drive it) + Canvas 2D ray-tree schematic (Ray debug) · KaTeX for the maths · no build step, no external assets.