vidar.games

Radiance cascades GI

2026-10-31 ยท Discuss on X

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

When to use it

Top-down and side-view games with many small lights: torches, spells, glowing pickups.

New breakdowns go out on X first: @vidar_0208. If this one helped, you can buy me a coffee.

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.