Expand description
The frame’s lights, packed for the shaders and clustered on the CPU.
A forward renderer shades a fragment from the lights that can reach it, and forward clustered rendering answers “which lights reach this fragment” by cutting the view into a grid of cells — tiles in screen space, logarithmic slices in depth — and giving every cell the indices of the lights that touch it. A fragment then reads exactly one cell.
The clustering here is plain CPU work over the packed records, producing the
u32 cell array and the LightGpu array a draw binds. It needs no compute
shader, which is what lets the same code serve WebGL2: a device with no
compute passes emits the very same bytes. The arrays are uploaded like every
other frame-wide array — a storage buffer where the device has those, texels
where it does not.
§The grid
Screen space is cut into cells of about ClusterConfig::cell_pixels pixels,
and depth into ClusterConfig::z_slices slices. Perspective slices are
equal in ln(depth) after the first slice’s fixed boundary; orthographic
ones are linear. Both mappings are monotonic and shared by the CPU assignment and the
fragment lookup; that shared mapping is the one correctness requirement,
because it is what keeps a light and the fragments it lights in one cell.
§The cell record
A cell is CELL_WORDS u32s: the count of lights in it, then their
indices, then padding to a whole texel. The indices point into the frame’s
LightGpu array, so a cell costs no light data of its own.
Lights are ranked by PackedLight::importance before assignment, so a cell
that overflows drops its weakest light and the first entries of every cell
are its strongest.
§A light at infinity is not in the grid
A directional light reaches every fragment, so assigning it to every cell
would spend a slot of each on the same record. Directional lights and the
frame’s ambient term live in LightingUniform instead — one frame-wide
uniform every lit fragment reads before the cell’s own lights — and the grid
holds only bounded ones. The uniform carries at most
MAX_DIRECTIONAL_LIGHTS of them; the rest of a frame’s directional lights
are dropped, weakest first, the way a full cell drops its weakest light.
Re-exports§
use glam::Mat4;🔒use glam::UVec4;🔒use glam::Vec2;🔒use glam::Vec3;🔒use glam::Vec4;🔒
Structs§
- Bounding
Sphere - The sphere clustering culls a light by.
- Cluster
Config - How a frame’s view is divided into cells.
- Cluster
Uniform - The clustering state a frame’s shaders read.
- Directional
Gpu - One directional light, as the frame-wide uniform carries it.
- Light
Clusters - One frame’s clustered light assignment, in storage the caller reuses.
- Light
Gpu - One light as the shaders read it.
- Lighting
Uniform - The frame-wide lighting every lit fragment reads before its cell’s lights.
- Packed
Light - A light packed for the shader, with the facts clustering needs.
Enums§
- Light
Kind - Which of the light record’s lanes a light uses.
Constants§
- CELL_
WORDS - How many
u32s one cell occupies: its count, its light indices, and the padding that rounds the record to a wholeRgba32Floattexel. - LIGHTS_
PER_ CELL - How many lights one cell holds: the record minus its count word.
- MAX_
DIRECTIONAL_ LIGHTS - How many directional lights one frame’s
LightingUniformcarries. - QUAD_
LIGHT_ CORNERS - How many corners a quad light’s polygon has.
Functions§
- depth_
slice - The depth slice containing
depth, mirrored bycluster_depth_slicein the built-in shader. - insert 🔒
- Append
indexto the cell atcell, dropping it when the cell is full. - screen_
cells 🔒 - The x and y cells a light’s sphere covers, as inclusive ranges.