You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Renderer: shadow cluster cull uses the CAMERA frustum, not the light's (off-screen casters lose shadows) #652
VirtualGeometryShadow::RenderCascade (OloEngine/src/OloEngine/Renderer/VirtualGeometry/VirtualGeometryShadow.cpp) dispatches VirtualClusterCull.comp for each CSM cascade but never sets u_ViewProjection. The cull shader's 6-plane frustum test (VirtualClusterCull.comp:295-311) is not gated on u_OrthoMode, so it runs in the shadow pass too — using whatever view-projection is currently in the camera UBO (binding 0), which is the main CAMERA's.
Consequence: a cluster that is outside the camera view but inside the light's cascade — an off-screen shadow caster — is culled from the shadow map, so its shadow is missing from the visible frame.
Not visible in VirtualGeometryStress.olo (every dragon is on-screen), which is why it hid. Shows up when a caster is behind/beside the camera casting into view.
Fix sketch
Either upload the light's render-origin-relative VP (lightVPRel, already computed in RenderCascade for orthoErrorScale) into the camera UBO before the cull dispatch, or add a dedicated cull-frustum uniform the shadow path sets. Needs a test proving an off-screen VirtualMeshComponent still casts into the visible frame (the existing shadow tests all keep the caster on-screen).
Could not reproduce — reverting the speculative fix and closing
I built the fix (dedicated u_ShadowViewProjection for the ortho/shadow cull) and a test to prove it, and the test would not fail without the fix. Following the evidence rather than the hypothesis.
What I verified, on a GPU, with the shader fix reverted and the shader cache fully nuked (confirmed real recompile), and with every classic mesh stripped from the scene so ONLY the virtual sphere can put depth in the CSM:
An off-screen shadow caster (a virtual sphere placed well above the camera's view frustum, inside the light's cascade, sun straight down) still casts its shadow correctly. The CSM contains its depth.
So the described symptom — "off-screen casters lose their shadow" — does not occur. The geometry was sound (radius-1 sphere at y=8, camera frustum top ~y=5.6 at that depth, so it is genuinely outside the camera view), which points at the premise being wrong, not the setup.
Why the premise was wrong (best current understanding): the frustum cull reads u_ViewProjection from the binding-0 camera UBO, but the shadow pass runs BEFORE ScenePass, and it appears the camera UBO is not populated with the camera's VP at that point (ScenePass uploads it later). So the shadow cull's frustum test operates on a stale/identity matrix and does not actually reject off-screen casters. Untidy — the shadow cull reading a camera-view UBO is wrong-by-construction — but with no observed effect.
Action: reverted the fix (shader + VirtualGeometryShadow::RenderCascade + the test). I am not shipping a fix for a bug I cannot demonstrate, nor a test that passes with the fix reverted — that is exactly the vacuous-test trap the rest of this work fought.
Closing as could-not-reproduce. If a future pass reorders the pipeline so binding-0 holds the camera VP during the shadow pass, this becomes a real latent bug and the dedicated-uniform fix (in the reverted diff) is the answer — worth a code comment at the cull's frustum section noting the dependency. Downgrading, not deleting the knowledge.
Activity
coderabbitai commentedon Jul 15, 2026
🔗 Related PRs
#126 - Feature/shadow_mapping [closed]
#465 - Fix/flaky fog visual evidence test [merged]
#538 - perf(renderer): early-out ShadowRenderPass when no light casts shadows (
#522) [merged]#587 - Camera-relative rendering + absolute-world pattern shaders (
#429) [merged]#628 - feat(renderer): clustered froxel lighting, budgeted shadow atlas, froxel volumetric fog (
#435) [merged]🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
drsnuggles8 commentedon Jul 15, 2026
Could not reproduce — reverting the speculative fix and closing
I built the fix (dedicated
u_ShadowViewProjectionfor the ortho/shadow cull) and a test to prove it, and the test would not fail without the fix. Following the evidence rather than the hypothesis.What I verified, on a GPU, with the shader fix reverted and the shader cache fully nuked (confirmed real recompile), and with every classic mesh stripped from the scene so ONLY the virtual sphere can put depth in the CSM:
So the described symptom — "off-screen casters lose their shadow" — does not occur. The geometry was sound (radius-1 sphere at y=8, camera frustum top ~y=5.6 at that depth, so it is genuinely outside the camera view), which points at the premise being wrong, not the setup.
Why the premise was wrong (best current understanding): the frustum cull reads
u_ViewProjectionfrom the binding-0 camera UBO, but the shadow pass runs BEFORE ScenePass, and it appears the camera UBO is not populated with the camera's VP at that point (ScenePass uploads it later). So the shadow cull's frustum test operates on a stale/identity matrix and does not actually reject off-screen casters. Untidy — the shadow cull reading a camera-view UBO is wrong-by-construction — but with no observed effect.Action: reverted the fix (shader +
VirtualGeometryShadow::RenderCascade+ the test). I am not shipping a fix for a bug I cannot demonstrate, nor a test that passes with the fix reverted — that is exactly the vacuous-test trap the rest of this work fought.Closing as could-not-reproduce. If a future pass reorders the pipeline so binding-0 holds the camera VP during the shadow pass, this becomes a real latent bug and the dedicated-uniform fix (in the reverted diff) is the answer — worth a code comment at the cull's frustum section noting the dependency. Downgrading, not deleting the knowledge.