Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commite8beb5604c

README.md

lightScattering

The render passes behind the VolumetricLighting component and the volumetricLighting module.

Two compute passes after lighting.

vol_scatter marches each view ray at half resolution, from the camera to the surface the ray hits — or to the medium's reach when it escapes to sky, since an unbounded march would integrate the whole horizon into every sky pixel. At each step it evaluates the medium's density, gathers the light arriving there, and accumulates the fraction sent toward the camera, attenuating by everything already crossed.

A surface-only renderer draws the pool of light a lamp throws on the floor but not the beam that carried it there, because nothing between the lamp and the floor is ever shaded. Marching the ray shades that space.

The lights are the scene's own. @scene.lights is the same buffer the lit passes read, so a light added, moved or recoloured at runtime changes the beam it throws with no bookkeeping here. It holds twelve vec4s per light — the light, then its punctual shadow record — read as raw words so no struct declaration has to mirror the engine's layout. A spot's cone gate is evaluated at the sample itself rather than at a surface, which is what makes the cone visible as a volume instead of only where it lands. Attenuation matches the lit passes' windowed falloff, so a beam and the pool of light it lands in agree about how far the source reaches. Area lights are skipped — their contribution to a medium is an integral over the rect rather than a single direction.

The sun's contribution is gated by a short march back toward it, tested against the depth buffer. That is what turns uniform haze into shafts: the medium is lit only where the path back to the sun is clear, so an occluder carves a dark volume out of the beam rather than only shading the surface behind it. Because the test reads the depth buffer, an occluder outside the view frustum does not cast into the medium.

Scattering follows Henyey-Greenstein, so the medium brightens toward a source you are looking at and stays dim when it is behind you — the difference between haze that reads as air and haze that reads as a grey wash.

Each ray's first sample is offset by a per-pixel fraction of a step, trading the banding a fixed step pattern produces for noise.

vol_composite upsamples that result and composites it. The taps are weighted by how closely their depth matches the full-resolution pixel's, which keeps a beam from bleeding across a silhouette — the halo a plain bilinear upsample leaves behind.

The camera comes from @frame.camera, the camera of the render target being drawn, and the forward matrix is derived by inverting it in the shader rather than passed in from script — so an offscreen capture or an RTT camera marches through its own matrix at its own resolution.

Parameters arrive through the vol_params buffer, packed by the volumetricLighting module.