3.4 KiB
Shadows (shadow mapping)
Shadows are off by default and are enabled by designating a single casting light:
app.scene.set_shadow_caster(Some(index)); // packed index — see the pitfall below
app.scene.set_shadow_caster(None); // shadows off (default)
Only a directional or spot light can cast shadows. A point light index disables the shadow pass (cubemap shadows are out of scope).
⚠️ The packed-index pitfall
set_shadow_caster takes the light's index in the packed array (directionals first,
then point, then spot — recalled in Lights).
Index 0 is the default +Z directional pre-loaded by Lights::new(), not necessarily
your light. Symptom of a wrong index: the shadow camera looks in an unexpected direction and
misaligned objects occlude each other (blackened objects, ghost shadows).
Two ways to avoid it:
-
Clear the list before adding yours — your light becomes index 0:
app.scene.clear_lights(); // removes the default +Z app.scene.add_directional_light(dir, [1.0, 0.98, 0.92], 1.6).unwrap(); app.scene.set_shadow_caster(Some(0)); // now it really is YOUR lightThis is the technique in
shadow_test.rs. -
Count the indices — if you keep the default light and add yours, it lands at index 1:
app.scene.add_directional_light(toward_light, [1.0, 0.98, 0.92], 1.5).unwrap(); // → index 1 app.scene.set_shadow_caster(Some(1)); // this is the demo's warm light that castsThis is the technique in
demo.rs.
How it works (to understand the limits)
Each frame, if a caster is active, the engine runs two passes (technical details in FRAME_LOOP):
- Shadow pass: the scene is rendered as seen from the light (depth-only
shadow_shader.wgslshader) into a 1024²Depth32Floatshadow map (size configurable viaSHADOW_MAP_SIZE), with a depth bias (slope-scaled + constant) to avoid shadow acne. - Color pass: the
standardfragment shader re-projects each fragment into light space and compares its depth against the map via a 3×3 PCF (softened shadow edges).
Things to know:
- Directional light: the shadow frustum is orthographic, centered on the scene center
(
SHADOW_SCENE_CENTER, radiusSHADOW_SCENE_RADIUS = 5.0by default). Objects far from the origin may fall outside the frustum and stop casting. - Spot light: the light's cone naturally bounds the shadow.
- Only one light casts at a time (no multi-light shadows).
- Shadows only affect meshes rendered by
standardin lit mode — a renderer in unlit mode (see Materials & textures) receives none.
Tuning shadow rendering
The constants SHADOW_MAP_SIZE, SHADOW_DEPTH_BIAS, SHADOW_SCENE_RADIUS,
SHADOW_SCENE_CENTER are exposed in wsg_lib::utils (defaults: 1024, 0.006, 5.0, origin).
Tuning tips:
- Speckled shadow edges (acne): raise the bias.
- Peter-panning (shadow detached from the object): lower the bias.
- Shadow clipped at the scene edge: raise the frustum radius (directional).
- Shadows too blurry, want them crisper: raise the map size.