Skip to content

Add configurable shadow focus target for directional cascades - #355

Open
keithwill wants to merge 1 commit into
ProwlEngine:mainfrom
keithwill:shadow-focus-target
Open

Add configurable shadow focus target for directional cascades#355
keithwill wants to merge 1 commit into
ProwlEngine:mainfrom
keithwill:shadow-focus-target

Conversation

@keithwill

Copy link
Copy Markdown
Contributor

Directional-light shadow cascades were always centered on the rendering camera's position, which assumes a first-person view. In a third-person game the player character is a distant focal point, so the character lands in a low-resolution far cascade (or falls out of the cascade range entirely) while the resolution is spent on empty ground in front of the camera.

Add an optional per-camera Camera.ShadowFocus transform that cascades center on instead. It falls back to the camera's own position when unset or when the target's GameObject has been destroyed, so existing scenes and the editor's scene view (which builds a fresh temporary Camera) are unaffected.

The CPU side places cascades around the focus point in DirectionalLight.GetShadowMatrix, while two shader sites select a cascade by distance: SampleDirectionalShadow in Lighting.glsl and VolDirShadow in VolumetricFog.shader. Both have to move together or selection disagrees with placement and cascade seams appear, so the resolved point is uploaded as a new _ShadowFocusPos uniform and both sites measure from it. The uniform is written before the no-directional-light early-out so it stays fresh every frame. Camera-relative values (view direction, fog distance, specular) keep using _WorldSpaceCameraPos.

The point is resolved once per render into CameraSnapshot, so mutating the target mid-render can't desync placement from selection. Closest-N local shadow caster selection in SceneLightSystem.Reconcile uses the same point.

Known limitation, unchanged by this: the cascade ortho depth range is a fixed +/- cascadeDistance * 0.5 slab around the snapped focus point rather than a fit to the scene's casters, so tall occluders toward the light can clip out of the map. This relocates that behaviour with the focus point; it does not fix it.

Tests cover the resolver's fallbacks, that it tracks a moving target, that the cross-object Transform reference survives a scene save/load via Echo's intra-graph rewiring, that a cascade lands centered on its focus point to within half a texel, and that sub-texel movement of the focus produces an identical view matrix (the texel snapping that keeps shadow edges from shimmering).

The BananaMan sample now demonstrates it: a player stand-in with props sits 25 units out on a large ground plane, the camera is third-person, and F toggles the focus between the player and the camera.

Directional-light shadow cascades were always centered on the rendering
camera's position, which assumes a first-person view. In a third-person game
the player character is a distant focal point, so the character lands in a
low-resolution far cascade (or falls out of the cascade range entirely) while
the resolution is spent on empty ground in front of the camera.

Add an optional per-camera Camera.ShadowFocus transform that cascades center
on instead. It falls back to the camera's own position when unset or when the
target's GameObject has been destroyed, so existing scenes and the editor's
scene view (which builds a fresh temporary Camera) are unaffected.

The CPU side places cascades around the focus point in
DirectionalLight.GetShadowMatrix, while two shader sites select a cascade by
distance: SampleDirectionalShadow in Lighting.glsl and VolDirShadow in
VolumetricFog.shader. Both have to move together or selection disagrees with
placement and cascade seams appear, so the resolved point is uploaded as a new
_ShadowFocusPos uniform and both sites measure from it. The uniform is written
before the no-directional-light early-out so it stays fresh every frame.
Camera-relative values (view direction, fog distance, specular) keep using
_WorldSpaceCameraPos.

The point is resolved once per render into CameraSnapshot, so mutating the
target mid-render can't desync placement from selection. Closest-N local
shadow caster selection in SceneLightSystem.Reconcile uses the same point.

Known limitation, unchanged by this: the cascade ortho depth range is a fixed
+/- cascadeDistance * 0.5 slab around the snapped focus point rather than a fit
to the scene's casters, so tall occluders toward the light can clip out of the
map. This relocates that behaviour with the focus point; it does not fix it.

Tests cover the resolver's fallbacks, that it tracks a moving target, that the
cross-object Transform reference survives a scene save/load via Echo's
intra-graph rewiring, that a cascade lands centered on its focus point to
within half a texel, and that sub-texel movement of the focus produces an
identical view matrix (the texel snapping that keeps shadow edges from
shimmering).

The BananaMan sample now demonstrates it: a player stand-in with props sits
25 units out on a large ground plane, the camera is third-person, and F toggles
the focus between the player and the camera.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant