name: rendering/shadows description: 2D sprite cast-shadow system - Sprite-Lit-ShadowCaster shader, per-prefab opt-in, LightingDirector/LightingProfileSO-driven shadow strength, interior ShadowsOnlyRoof occluders.
Rendering - Shadows
Real URP directional-light cast shadows for every 2D sprite in the 3D world. Rotates with the sun driven by LightingDirector (which absorbed and replaced the old DayNightCycle.cs in the 2026-06-12 lighting rework — see .agent/skills/lighting/SKILL.md). Survives the Spine 2D migration without interface changes.
When to use this skill
- Adding shadow casting to a new sprite prefab (character, tree, furniture, prop).
- Modifying the shadow-casting shader, material, or URP asset configuration.
- Setting up / authoring a new interior prefab and its
ShadowsOnlyRoofchild. - Debugging shadow acne, Peter-Panning, or cascade popping.
- Wiring per-object shadow behaviour from an
ItemSO.CastsShadowoverride.
Components
- Shader:
Assets/Shaders/Sprite-Lit-ShadowCaster.shader- URP Forward pass + alpha-tested ShadowCaster pass. - Materials:
Assets/Materials/Sprites/DefaultSpriteShadowCaster.mat- default (_Cutoff = 0.5).Assets/Materials/Sprites/SmallPropShadowCaster.mat- variant with_Cutoff = 0.7for rings/potions.
- Per-prefab flag:
Renderer.shadowCastingMode = On+ material swap. Characters, trees, furniture, props. - ItemSO override:
ItemSO.CastsShadow(default true) drivesWorldItem.ApplyShadowCastingFromItemSO()at runtime. - Sun hook: the
ShadowStrengthAnimationCurve onLightingProfileSO(Assets/Resources/Data/Lighting/DefaultLightingProfile.asset), evaluated per frame byLightingDirector.ApplySun(parallel to theSunIntensitycurve). Decoupled intentionally so dawn/dusk can run dim-sun + dramatic-shadows. (WasDayNightCycle._shadowStrengthCurvebefore 2026-06-12.) - Received-shadow distance fade: handled inside the stylized-lit shaders via the 3-arg
GetMainLight(shadowCoord, positionWS, half4(1,1,1,1))overload — the 1-arg overload hard-cuts shadows at the last cascade edge instead of fading over the shadow distance. - Indoor occluder: Each interior prefab has a
ShadowsOnlyRoofchild (plane withShadowCastingMode.ShadowsOnly), sized inline to the interior footprint + shallow-sun margin. - URP config:
PC_RPAsset+Mobile_RPAsset- 2 cascades, 0.4 split, 80u distance, Soft Cascades On.
How to add shadows to a new prefab
- Swap its
SpriteRenderer.sharedMaterialtoDefaultSpriteShadowCaster(orSmallPropShadowCasterif the sprite is small/thin). - Set
shadowCastingMode = On,receiveShadows = true. - For Spine characters: use a
Spine-Skeleton-Lit-ZWritematerial instead, same two flags on theSkeletonAnimation.MeshRenderer. - For items: set
ItemSO.castsShadowon the ItemSO asset (default true; flip false for noisy small sprites).
How to author a new interior prefab roof
- Create Empty Child under the interior prefab's root, named
ShadowsOnlyRoof. - Add
MeshFilter(Plane primitive) +MeshRenderer. - Material:
DefaultSpriteShadowCaster(any material works - the quad is shadow-only, visible output is culled). MeshRenderer.shadowCastingMode = ShadowsOnly,receiveShadows = false.- Position at roof height above the floor (~4.2u for a standard 2.5x-human-height interior).
- Rotation X = 90 (plane faces down).
- Scale X/Z = interior footprint + shallow-sun margin (e.g. 10x10u interior -> scale 14x14).
Dependencies
- URP 17.3 (Universal Render Pipeline).
LightingDirector.cs+LightingProfileSO+TimeManager.cs(sun rotation + shadow strength + time-of-day).DayNightCycle.csis DELETED (absorbed by LightingDirector, 2026-06-12).- Spine 2D (forward-compat):
Spine-Skeleton-Lit-ZWrite.shaderalready in project.
Integration points
CharacterVisual- untouched. Shadow is a pass on the material the renderer already holds.ICharacterVisual- untouched. Shadow logic never touches gameplay code.FurnitureInteractable- untouched. Pure prefab setup.WorldItem- consumesItemSO.CastsShadowinInitialize().
Multiplayer
No networked surface. Shadows are per-client rendering. TimeManager.CurrentTime01 is already shared, so all clients compute the same sun direction - shadows are implicitly consistent across Host <-> Client, Client <-> Client, Host/Client <-> NPC.
Save / load
Zero surface. Pure visual, no serialization.
Known gotchas
- No DepthOnly pass in the shader. The sprite shader has only ForwardLit + ShadowCaster. If the project ever enables SSAO or any URP screen-space effect that relies on the camera depth prepass, sprites will not write to the depth texture and silhouettes will disappear from the depth-dependent effect. Fix when needed: add a standard URP DepthOnly pass (10-line modeled on URP
Unlit) with the same alpha clip as the ShadowCaster pass. - ShadowsOnlyRoof default size is 14x14. Interior prefabs larger than ~10u footprint will leak sun at shallow dawn/dusk angles. Verify per-interior in Play Mode at
TimeManager.CurrentTime01 = 0.25and0.75; resize the child quad in the Inspector where needed.
Open items (tunable after playtest)
Light.shadowNormalBiasdefaults to 0.8 - tune 0.5-1.5 if acne or Peter-Panning appears.DefaultSpriteShadowCaster._Cutoff = 0.5/SmallPropShadowCaster._Cutoff = 0.7- artists can override per material if a specific sprite clips wrong.- Per-interior
ShadowsOnlyRoofscale - default(14, 1, 14), resize if the interior footprint is larger or low-angle sun leaks. - Mobile
m_SoftShadowQuality = Medium- if jagged shadow edges are visible on-device, raise to High (2-3x fragment cost).
Out of scope (future work)
- Cloud / weather shadows beyond the overcast hook (
WeatherLightingBridge→LightingDirector.SetOvercastflattens shadow strength under a front; a real scrolling cloud-shadow cookie remains future work). - Moonlight shadows at night now come for free where the profile's
ShadowStrengthcurve is > 0 — the single directional light doubles as the moon (see lighting SKILL); tune via the profile, no second light needed. - Cross-quad meshes for wide-canopy trees.
- Normal-bias-by-sun-angle curve.
- Per-archetype custom
_Cutofftuning beyondItemSO.castsShadow.
See also
- Lighting skill (sun/moon driver, LightingProfileSO authoring, MWI* globals, stylized-lit shader recipe): .agent/skills/lighting/SKILL.md
- Design spec: docs/superpowers/specs/2026-04-19-2d-sprite-cast-shadows-design.md
- Implementation plan: docs/superpowers/plans/2026-04-19-2d-sprite-cast-shadows.md
- Wiki architecture page: wiki/systems/shadows.md (created in Task 17)