Post 102

Every Light Pulls Its Weight

Point and spot lights finally cast shadows. Cube shadows for points, cone views for spots, scheduled on a per-frame budget so a 200-light club rig keeps its framerate. Also the story of a bias that quietly deleted every shadow past the midpoint of a light's range, caught by the test that shipped with the feature.

Until this release, exactly one light in Lux could cast a shadow: the sun. Point lights and spotlights illuminated everything they touched, including the inside of objects’ silhouettes, with the serene confidence of a light that has never heard of occlusion. For a club rig or a stage wash, that meant every fixture read as a glow, never as a beam with consequences.

Now they all pull their weight. Point lights render a six-face cube shadow around themselves; spotlights render one view down their cone. Up to sixteen of each get a persistent home in a shared shadow atlas, assigned in scene order so a light keeps its slot from frame to frame and nothing flickers when you add fixture number seventeen.

The budget, or how 200 lights stay fast

A point light is six shadow renders. Two hundred point lights would be twelve hundred, which is not a framerate, it is a slideshow with commentary. So cube refreshes are scheduled: a fixed number of cubes re-render each frame, round-robin across the active set, while the rest keep their previous faces. A static light’s stale cube is pixel-identical to a fresh one, so you cannot tell who re-rendered this frame, which is the point. Spotlights are one view each and refresh every frame. The night-club patch with its 200 movers needed no edits at all; the scheduler is the feature.

Culling got the grown-up treatment too. Each shadow view runs the same GPU meshlet cull as the main camera, and unlike the sun’s cascades it gets a real eye position, so cone culling rejects geometry facing away from the light instead of shrugging.

The shadow that wasn’t there

The feature shipped with a test: a box between a point light and the floor must darken the floor. It passed. The spotlight version of the same test did not, and the reason is worth the devlog. Shadow depth comparisons used a small fixed bias in projected depth units, the same trick the sun’s shadows use. But the sun’s projection is orthographic and a punctual light’s is perspective: past the midpoint of the light’s range, perspective depth compresses so hard that the entire gap between an occluder and the floor behind it was smaller than the bias. The comparison concluded that everything was lit. The point-light test had passed by a margin of 0.0006.

The fix compares in metres instead. Stored depth is unprojected back to linear distance, and the bias is derived from what it actually protects against: the size of one shadow texel at that distance, scaled by the slope of the surface, plus two centimetres of humility. Steep walls under grazing light no longer self-shadow, contact shadows stay attached, and the same formula serves both a 90-degree cube face and a narrow spot cone because it reads the projection’s own focal length rather than assuming one.

What it buys you

Aim a spotlight at a dancer and the dancer blocks it, like physics intended. Drop a point light inside a sculpture and the shadows radiate in all six directions. Stack two hundred of them and the scheduler quietly decides who re-renders tonight. Shadows land the same frame the light appears, behind the same beat as everything else in /devlog/the-case-of-the-vertical-floor/’s newly trustworthy sun.

← All posts