Introduction

Stand a glass of water in sunlight and a bright, wobbly patch of light appears on the table below. That patch — a caustic — forms because the curved glass bends, focuses, and scatters the rays. To a human eye it is mundane; to a computer graphics renderer, it was, for decades, nearly impossible to simulate cheaply.

The breakthrough came in 1996 when Henrik Wann Jensen introduced photon mapping, a two-pass algorithm that separates light transport into two distinct phases: shooting photons and gathering them. Instead of tracing individual rays from the camera and hoping they happen to hit a light source, photon mapping pre-computes where light energy lands in the scene — and then queries that cache at render time.

The result is a method that handles caustics, color bleeding, participating media, and indirect illumination in a unified way, and that became the backbone of production renderers used in films, games, and architectural visualization for more than two decades.

Watch Caustics Form

The canvas below simulates photon mapping in 2-D. A point light at the top shoots photons downward through a convex glass lens. Each photon refracts, travels, and lands on the floor — where it is stored as a dot in the photon map. During the gather pass, each floor point queries its neighborhood and sums the photon energy it finds.

<!-- {{c_html_intro}} -->
<div class="controls">
  <label>{{lbl_photons}} <span id="photon-val">400</span>
    <input type="range" id="photon-count" min="50" max="1200" step="50" value="400">
  </label>
  <label>{{lbl_radius}} <span id="radius-val">18</span>px
    <input type="range" id="gather-radius" min="6" max="40" step="2" value="18">
  </label>
  <button id="shoot-btn" type="button">{{btn_shoot}}</button>
  <button id="reset-btn" type="button" class="ghost">{{btn_reset}}</button>
</div>
<canvas id="scene" width="460" height="300"></canvas>
<div class="status" id="status">{{hint_initial}}</div>
/* {{c_css_intro}} */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; background: #fff; }
.controls {
  display: flex; flex-wrap: wrap; gap: .5rem .9rem;
  align-items: center; margin-bottom: .5rem;
}
label { font-size: .85rem; display: flex; align-items: center; gap: .4rem; }
input[type=range] { width: 90px; cursor: pointer; }
button {
  font: 600 13px system-ui; padding: .4rem .85rem;
  border: 1px solid #1d3557; background: #1d3557;
  color: #fff; border-radius: 7px; cursor: pointer;
}
button.ghost { background: #fff; color: #1d3557; }
canvas { display: block; border: 1px solid #dce3ea; border-radius: 8px; max-width: 100%; }
.status { font-size: .9rem; font-weight: 600; min-height: 1.3em; margin-top: .4rem; color: #1d3557; }
// Code not found

Crank up the photon count and the caustic sharpens — you are literally trading computation for accuracy. Widen the gather radius and the image blurs as each point averages over a larger neighborhood. This trade-off between speed and quality is the defining tension of the algorithm.

The Real Complexity

Photon mapping is not a polynomial-hardness story — it is a statistical approximation with well-understood error bounds, and understanding those bounds is what makes the algorithm tunable in practice.

Pass 1 — photon emission: Emit nn photons from each light source. Each photon bounces using Russian roulette to decide whether to be absorbed or scattered. Storing all photons in a k-d tree costs O(nlogn)O(n \log n) time and O(n)O(n) space.

Pass 2 — radiance estimate: For each point xx on a surface, find the kk nearest photons in the map. The radiance estimate is:

L^(x)=1πr2i=1kΦifr(x,ωi,ωo)\hat{L}(x) = \frac{1}{\pi r^2} \sum_{i=1}^{k} \Phi_i \cdot f_r(x, \omega_i, \omega_o)

where rr is the radius of the sphere containing the kk photons, Φi\Phi_i is the photon power, and frf_r is the BRDF. Each k-nearest-neighbor query costs O(klogn)O(k \log n).

Bias and variance: The estimate is biased (it smooths over a finite area), but the bias shrinks as r0r \to 0. The variance shrinks as 1/k1/\sqrt{k}, so doubling the photon count halves the noise — much like Monte Carlo sampling in quantum simulation.

Progressive photon mapping (2008): Jensen's student Toshiya Hachisuka removed the memory bottleneck by streaming photons in multiple passes, shrinking rr slightly each pass. The result converges to the correct answer with bounded memory — a key insight borrowed from stochastic approximation theory.

Where It Matters

Photon mapping's two-pass idea proved powerful enough to influence rendering far beyond its original form:

  • Film VFX: Pixar, ILM, and Weta Digital used photon-map variants for underwater caustics, glass-heavy scenes, and volumetric effects in films such as Finding Nemo and The Lord of the Rings.
  • Architectural visualization: rendering a daylit atrium with skylights is exactly the kind of indirect-illumination problem photon mapping handles naturally.
  • Game baking: offline photon mapping bakes indirect light into lightmaps that the GPU reads in real time — a trick still used in modern game engines.
  • Medical and scientific imaging: simulating light propagation through tissue or atmosphere uses the same transport equations that photon mapping solves.
  • Path tracing hybrids: modern renderers like Arnold and RenderMan combine photon maps with Monte Carlo path tracing — using the photon map only for caustics where path tracing converges too slowly.

The deeper idea — precompute a spatial cache of energy, then query it — shows up in many algorithms far outside graphics: photon mapping is one of the clearest examples of trading offline work for online speed.

Conclusion

Photon mapping solved a problem that seemed intractable by refusing to answer it directly. Instead of asking "where does light go?" at render time, it asks that question once, offline, stores the answer in a spatial index, and then queries that index cheaply for every pixel. The two-pass split — emit, then gather — is what makes caustics and global illumination affordable.

The lesson generalizes: when a computation is expensive but its result is reusable across many later queries, precompute and cache. Photon mapping is, at its core, a rendering algorithm with the soul of a database index — and that idea will outlive every specific technique built on top of it.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/photon-mapping/Content licensed under CC BY-NC 4.0.