Visitar URL original
fix(core): fit the shadow map to the visible frustum at high pitch (3/5) by charlieforward9 · Pull Request #10893 · visgl/deck.gl · GitHub
Skip to content

fix(core): fit the shadow map to the visible frustum at high pitch (3/5) - #10893

Merged
charlieforward9 merged 11 commits into
masterfrom
fix/shadow-frustum-high-pitch
Oct 26, 2026
Merged

charlieforward9 merged 11 commits into
masterfrom
fix/shadow-frustum-high-pitch

Conversation

@charlieforward9

@charlieforward9 charlieforward9 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

For #9446. Part 3 of 5 of the shadow fixes; parts 1 and 2 (#10892, #10891) have merged. #10903 and #10913 follow.

The shadow map is fitted to where the screen corners hit the ground. Near the horizon (pitch around 70° and up), those points run far away or land behind the camera. Shadows then blur, vanish, or render black. The shadow map is now fitted to the visible part of the view.

Change List

  • shadow module: fit the shadow map to the visible view
  • Unit tests
  • Render test shadow-effect-high-pitch with new golden image

Top: master. Bottom: this PR. Pitch 65 / 70 / 72 / 85.

shadow-high-pitch-before-after

The new tests fail on master. Existing render tests are unchanged.

TODO

charlieforward9 and others added 3 commits October 22, 2026 07:02
… off

EffectManager reuses the existing LightingEffect and calls setProps when
a new one is passed. Turning the last `_shadow` light off kept the
shadow module registered as a default module, but getShaderModuleProps
stopped passing the dummy shadow map, so shadow_uShadowMap0/1 were
unbound and every layer skipped drawing. Call cleanup() from setProps
when no light casts shadows, so layers are rebuilt without the module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Layer._drawLayer applies `getPolygonOffset` (default [0, -layerIndex * 100])
in every pass. The shadow map stores gl_FragCoord.z, which includes the
offset, so the k-th layer drawn in the shadow pass stored a depth
k * 100 * 2^-16 closer to the light. From the third layer on, layers fell
partly or fully into their own shadow. ShadowPass now sets depthBias: 0
so the shadow map is drawn without the offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getViewProjectionMatrices fitted the light frustum to the points where the
viewport corner rays hit the ground. With the default fov, the top corners
reach the horizon at pitch ~71.6. Just below it the hits are far beyond the
camera's far plane (light frustum 20 x 28 km at pitch 72, 63 m texels).
Above it they are behind the camera, so the shadow map no longer covers the
visible scene. Shadows disappear, and fragments past the light frustum's far
plane are always shadowed (black areas).

Fit the light frustum to the view frustum clipped to z >= 0 instead: the
frustum corners above the ground plus the points where frustum edges cross
the ground. When every corner ray hits the ground before the far plane, this
gives the same points as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable new issue was found.

Summary

Fits geospatial shadow maps to the visible view above ground, including where the far plane meets the ground at high pitch.

  • High-pitch maps fit shadows to the visible ground.

Reviews (5) · Last reviewed commit: "Merge branch 'master' into fix/shadow-fr..." · Reviewed by Greptile

@coveralls

coveralls commented Oct 22, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 84.16% (+0.001%) from 84.159% — fix/shadow-frustum-high-pitch into master

@ibgreen ibgreen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed; the high-pitch shadow-frustum fit and regression coverage are ready to approve.

# Conflicts:
#	test/modules/core/passes/shadow-pass.spec.ts
#	test/render/test-cases/effects.spec.ts
…-high-pitch

# Conflicts:
#	test/render/test-cases/effects.spec.ts
@charlieforward9 charlieforward9 changed the title fix(core): fit the shadow map to the visible frustum at high pitch fix(core): fit the shadow map to the visible frustum at high pitch (3/5) Oct 26, 2026
@charlieforward9
charlieforward9 changed the base branch from master to fix/lighting-effect-shadow-off October 26, 2026 04:00
Base automatically changed from fix/lighting-effect-shadow-off to master October 26, 2026 04:38
# Conflicts:
#	test/render/test-cases/effects.spec.ts
@charlieforward9
charlieforward9 merged commit d610287 into master Oct 26, 2026
8 checks passed
@charlieforward9
charlieforward9 deleted the fix/shadow-frustum-high-pitch branch October 26, 2026 04:55
chrisgervang pushed a commit that referenced this pull request Oct 26, 2026
…/5) (#10893)

* fix(core): release shadow resources when LightingEffect turns shadows off

EffectManager reuses the existing LightingEffect and calls setProps when
a new one is passed. Turning the last `_shadow` light off kept the
shadow module registered as a default module, but getShaderModuleProps
stopped passing the dummy shadow map, so shadow_uShadowMap0/1 were
unbound and every layer skipped drawing. Call cleanup() from setProps
when no light casts shadows, so layers are rebuilt without the module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): draw the shadow map without per-layer polygon offset

Layer._drawLayer applies `getPolygonOffset` (default [0, -layerIndex * 100])
in every pass. The shadow map stores gl_FragCoord.z, which includes the
offset, so the k-th layer drawn in the shadow pass stored a depth
k * 100 * 2^-16 closer to the light. From the third layer on, layers fell
partly or fully into their own shadow. ShadowPass now sets depthBias: 0
so the shadow map is drawn without the offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): fit the shadow map to the visible frustum at high pitch

getViewProjectionMatrices fitted the light frustum to the points where the
viewport corner rays hit the ground. With the default fov, the top corners
reach the horizon at pitch ~71.6. Just below it the hits are far beyond the
camera's far plane (light frustum 20 x 28 km at pitch 72, 63 m texels).
Above it they are behind the camera, so the shadow map no longer covers the
visible scene. Shadows disappear, and fragments past the light frustum's far
plane are always shadowed (black areas).

Fit the light frustum to the view frustum clipped to z >= 0 instead: the
frustum corners above the ground plus the points where frustum edges cross
the ground. When every corner ray hits the ground before the far plane, this
gives the same points as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d610287)
@chrisgervang

Copy link
Copy Markdown
Collaborator

Released in deck.gl 9.4.1. Thanks @charlieforward9!

@chrisgervang chrisgervang mentioned this pull request Oct 29, 2026
16 of 47 tasks
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.

4 participants