Flatland / Light transport
Starting the Ruby engine…
The model ↘
← Project overview

Rendering, with the hidden steps exposed

The anatomy of light.

A scene reduced to two dimensions. Every sampled direction, failed connection, and weighted contribution remains visible. Three path estimators and a surface photon map reveal two ways to reconstruct the same light field.

Scene settings +

Changes reset every estimator. Reflectance scale and emitter length are fixed in the calibration scene. Finite bounce limits omit longer paths from all four methods. A wider photon kernel smooths noise and detail; it does not remove bias. The photon map freezes at its budget while PT/MIS/LT can continue sampling.

The transport plane

Diffuse courtyard

2D cross-section · angular sensor
Camera pathLight connectionEmitted pathBlocked
No path yet

The scene is a diagram, not a 3D beauty render. Path colors identify construction strategy; the sensor strips below show computed radiance.

Path samples and stored photons

What reaches the sensor?

Each strip contains 64 angular bins. Every estimator has the same bounce cutoff. The blue photon-map curve adds finite-radius smoothing and eight fixed camera quadrature directions per bin, so close agreement is approximate. Equal launches are not equal work: explicit connections add visibility rays. Changing exposure only changes the display.

Angular radiance profile · left to right follows the camera field of view

Zeros are evidence.

Blocked and missed paths remain in the denominator. Light tracing sums every splat into its own bin and divides by all emitted paths.

± The displayed uncertainty is one estimated standard error of the image-wide mean, computed over independent launches—not a per-pixel guarantee. Photon mapping reuses a correlated map, so no independent-launch standard error is shown for it.

A small model with explicit rules

What each picture estimates.

01

Integrate in the right dimension.

Directions live on a circle. A diffuse surface uses f = ρ / 2 and p(θ) = cos θ / 2, since the hemisphere cosine integral is 2. The angular-to-length conversion is dθ = |cos θₗ| ds / r. Three-dimensional factors such as π and r² do not carry over unchanged.

02

Connect without counting twice.

Path tracing may reach the emitter by chance. Next-event estimation samples a point on it explicitly. The MIS mode uses the power heuristic with both densities expressed per radian; an emitter reached by continuation receives the complementary weight. This is direct-light MIS, not full bidirectional path tracing.

03

Reverse construction, preserve the observable.

Light tracing begins at a line emitter, reflects, then connects each diffuse vertex to the camera. The camera reports mean radiance over each angular bin. It is an ideal point receiver with a 1D angular film, not the original finite-aperture camera or a complete camera radiometry model.

04

Replace connections with a density estimate.

Photons store incoming flux before the receiving surface scatters it. Gather only on the same object and side; divide by all launched photons and the actual surface support length, then apply ρ / 2. A circle uses arc length and a line uses length: this 2D kernel is not a 3D disk of area πr². Larger radii hide noise but blur lighting boundaries. This is fixed-radius surface photon mapping, not VCM or progressive photon mapping.

Companion to RadianceLab

First inspect a path.
Then study an estimator.

RadianceLab's photon-primitive studies examine how sampling densities, missed intersections, and small Jacobians affect variance. Flatland isolates the earlier mechanics: constructing a path, testing a connection, accounting for its density, and either accumulating weighted path contributions or reconstructing illumination from a photon density.

Version & scope

This corrected Ruby engine is a new surface-transport edition of the original Raylib visualizer. The Raylib desktop interface has draggable camera controls, material and phase-function inspectors, glass, rough reflection and medium experiments. Those use older tracer classes with known numerical inconsistencies; they are not the core running here. This browser app runs the corrected Ruby source directly in WebAssembly, including the new photon map. It retains diffuse surfaces and an angular pinhole camera; full BDPT, VCM and MLT are not implemented in either edition.

Inspect the actual Ruby source ↗ Read the numerical checks ↗