Shadows
The engine supports real-time shadow mapping for all three light types: directional, spot, and point lights. Shadows use depth-only render passes, PCF soft filtering, and configurable resolution/bias.
Enabling Shadows
Set CastShadow = true on any Light component:
Shadows work with all light types:
Shadow Limits
Lights beyond these limits still illuminate the scene but do not cast shadows.
Configuration API
All shadow parameters can be tuned at runtime from C#:
Shadow Map Resolution
Higher values produce sharper shadows but use more VRAM. The resolution applies to all shadow maps (directional, spot, and point cubemap faces).
Shadow Bias
Controls the depth offset to prevent shadow acne (dark striping on lit surfaces):
- Too low: shadow acne (dark stripes on surfaces)
- Too high: peter panning (shadow detaches from object base)
Shadow Bounds
Controls how large an area the directional light shadow covers:
Smaller values give higher shadow detail but cover less of the scene. The directional shadow frustum follows the camera using a stable frustum-fitting algorithm that snaps the shadow center to the texel grid in light space, preventing shadow shimmering when the camera moves.
How It Works
Directional and Spot Shadows
- A depth-only render pass renders the scene from the light's perspective into a 2D depth texture
- Directional lights use an orthographic projection; spot lights use a perspective projection matching the cone angle
- The main fragment shader samples the shadow map and compares fragment depth to determine if the pixel is in shadow
- A 5x5 PCF (Percentage Closer Filtering) kernel samples 25 neighboring texels for soft shadow edges
Point Light Shadows
- Each point light renders the scene 6 times (one per cubemap face: +X, -X, +Y, -Y, +Z, -Z)
- The fragment shader writes linear distance from the light (not raw depth) for correct omnidirectional comparison
- The main shader samples the cubemap using the fragment-to-light direction vector
- A 20-sample disk PCF kernel provides soft shadow edges