Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit8d45df8664

README.md

shadows

The shadow-sampling interface every surface shader calls instead of writing its own PCF loop: compute_directional_shadow_factor for the sun (its virtual shadow map's pages), compute_spot_shadow_factor, compute_point_shadow_factor and compute_area_shadow_factor for punctual lights. Each answers 1.0 for fully lit and 0.0 for fully shadowed.

A shader that walks the punctual lights (zero_point_light(i)) rather than naming one light asks compute_punctual_shadow_factor(index, world_pos, normal) instead, which reads the row's kind and answers for whatever shape it casts in, and weighs the answers with zshadow_light_reach(index, world_pos, normal, object_channels) — how much direct light that row actually puts on a surface on those light channels, zero exactly where it puts none, the channel mask included. zshadow_punctual_probe and zshadow_directional_probe go one level under a factor, returning the depth the fragment carries in a light's map beside the depth the map already held there, loaded rather than compared.

Routing through these is what keeps a shader in step with the renderer — the page levels, map resolution and filter width come from the bound frame data rather than from constants baked into the shader. The directional call selects the level the receiver's screen footprint asks for, and inside a hero box (renderer.setShadowHero) a level finer by the box's level bias.

// @domain: surface
#include "@builtin::shaderModules.shadows"

fn fragment(input: FragmentData) -> vec4<f32> {
    let n = normalize(input.world_normal);
    let lit = compute_directional_shadow_factor(input.world_position, n, uniforms.light_direction);
    return vec4<f32>(vec3<f32>(lit), 1.0);
}