Radiance cascades GI
Same courtyard, same budget: 6.45 M rays per frame on each side.
Left: 28 random rays per texel, full length.
Right: radiance cascades. Short rays near each probe, longer, sparser ones further out.
The problem
Soft global illumination in 2D means casting rays from every pixel in every direction. With a fixed ray budget, you either cast few rays (noise) or few probes (blur). The left half spends the budget the obvious way: 28 random rays per texel, each one running the full distance across the courtyard. The light is there, but it crawls with noise.
How it works
Radiance cascades split each ray into distance bands.
Level 0: a probe every 2 px, 4 directions, rays only 1 px long.
Each next level: probes 2x further apart, 4x more directions, ray interval 4x longer.
Near light changes fast with position and slowly with angle. Far light is the opposite. So close range gets many probes with few directions, far range gets few probes with many directions.
After tracing, the levels merge from the top down. Each probe adds the light its short ray saw to the light the next level saw beyond it, interpolated from the 4 nearest upper probes. The result is a 640x360 radiance field.
What I hit
The equal-budget claim needed actual arithmetic. With a 1280x720 cascade texture and 7 levels, cascades trace 6,451,200 rays. 640 x 360 texels x 28 rays is also 6,451,200. Brute-force rays run full length, so they take more sphere-tracing steps each; the comparison is ray count, not time.
The repo is 2D. Occluders act as infinitely tall walls in the light calculation. I sample the 2D field on the floor and 8 cm off each wall, with my own height falloff exp(-0.55 y). That falloff is a look choice, not part of the method.
My build
The repo's GLSL used as-is (cascade info, trace, merge, radiance field), loaded in the same order as its demo. I replaced the paint brush with an analytic SDF of my courtyard: 8 walls and crates, 5 pillars, 4 emitters, 80 px per metre. A moving wisp and a flickering brazier make the shadows move.
Trade-offs
- Soft shadows without random noise, from the same ray count
- Cost does not depend on how many lights there are
- Memory for 7 cascade levels
- 2D only here; 3D versions exist but are a much bigger build
- No temporal accumulation on either side, so the left is raw brute force, not the best denoised version
When to use it
Top-down and side-view games with many small lights: torches, spells, glowing pickups.
Source and credits
Technique: Radiance Cascades by Alexander Sannikov.
Repo: github.com/simondevyoutube/Shaders_RadianceCascades (MIT license)
Code: the cascade, merge and radiance-field shaders (cascades.glsl, compute-cascade, merge-cascades, radiance-field) and their helper files are used unchanged; the pass order follows the repo's demo.js. The courtyard SDF, the brute-force comparison, the 3D scene and all models are my own. No assets or screenshots from the repo.
Topic inspired by @codetaur's and @Chen_Yi_Ethan's radiance cascades posts.