Skip to content

Shadow receiver bias in world units for every cascade reader - #207

Merged
proggeramlug merged 1 commit into
mainfrom
fix/shadow-bias-gi-readers
Nov 12, 2026
Merged

proggeramlug merged 1 commit into
mainfrom
fix/shadow-bias-gi-readers

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #199, which moved the main pass's shadow receiver bias from a constant in NDC to one shadow texel of world distance. Every other reader of the directional cascades still subtracted a constant in NDC, and all of them scale the same way: the cascades pancake their depth range to the scene bounds, so a 600 m ground plane turns 0.002 into a metre and anything closer than that to a receiver stops casting on it (a deck no longer shadowing the hull under it, contact shadows floating).

This PR gives each remaining reader the same world-sized bias, measured from the cascade's own orthographic view-projection so no new uniform is needed: the x row of an ortho VP has length 2 / width in metres and the z row 1 / depth range, so one texel is 2 / (|x row| · map size) metres and |z row| times that in NDC.

Reader Was Now
GI mesh-card relight (CARD_LIGHT_WGSL) 0.002 NDC, position only 1.5-texel normal offset along the card normal + 1 texel depth (flags.x is now a texel count)
WSRC probe bake (WSRC_BAKE_WGSL) 0.002 NDC 2 texels depth; probes sit in free space, no normal to offset along
Path tracer hybrid sun (sun_vis_cascade) 0.002 NDC, caller added 2 cm 1.5-texel normal offset + 1 texel depth; takes the normal
Planar reflection pass (refl_sun_shadow) 0.001 NDC 1 texel depth (it already had the main pass's normal offset)
Transmitted shadow resolve 0.0015 NDC 2 transmitted-map texels
Transmitted shadow caster hidden-glass test 0.00075 NDC 2 opaque-map texels (the draw uniforms are now visible to the fragment stage for light_vp)
Virtual shadow maps, scene helper 0.001 NDC 1 virtual texel (receiver already normal-offset)
Virtual shadow maps, material helper 0.001 NDC 2 virtual texels

The hardware GI variants (CARD_LIGHT_HW_WGSL, WSRC_BAKE_HW_WGSL) trace their sun visibility and never read the bias, so they are unchanged. The SSGI probe-trace uniform still carries a 0.002 in shadow_params.x, but no shader reads it. Point and spot light VSM pages keep their perspective-depth bias; that one does not pancake.

Test plan

  • cargo test --release --no-fail-fast in native/shared: 509 unit tests, the golden suite and the runtime suites pass with no golden image changed. The one failure, visibility_shading_matches_forward_reference (max delta 2 against a 1-LSB bound), fails identically on main at 49cf675 on this machine (148 changed channels in both).
  • cargo fmt --check, ./scripts/ci-check.sh --quick --component lint and --component contracts (file-size ratchet included) pass.
  • The Deep (600 m sea plane, SSGI on the hardware ray-query backend): hold, cargo deck and lots scenes rendered against this branch and against main are visually identical at 1600×900. The user-visible fix was shadows: the receiver bias is a shadow texel of world distance, not a constant in NDC #199; this one brings the bounce-light and reflection feeds in line with it.
  • CI

Summary by CodeRabbit

  • Bug Fixes

    • Shadow depth bias now scales with shadow-map texel size and cascade depth, improving consistency across shadow maps and scenes.
    • Shadow receivers receive a small normal-based offset to help reduce shadow artifacts.
    • Transmitted-shadow blocker and receiver tolerances now adapt to map resolution.
  • Documentation

    • Clarified that shadow-bias settings are measured in shadow texels.

Follow-up to #199, which sized the main pass's receiver bias as one
shadow texel of world distance instead of a constant in NDC. The other
readers of the directional cascades still subtracted NDC constants, and
all of them scale with the pancaked depth range: over a 600 m ground
plane 0.002 is a metre, so anything closer than that to a receiver stops
casting on it.

Each reader now measures its bias from the cascade's own orthographic
view-projection (x row = 2 / width in metres, z row = 1 / depth range),
so no new uniform is needed:

- GI mesh-card relight: 1.5-texel normal offset along the card normal
  plus one texel of depth; flags.x is a texel count now.
- WSRC probe bake: two texels (free-space probes, no normal).
- Path tracer hybrid sun: normal offset plus one texel, takes the normal.
- Planar reflection pass: one texel (it already had the normal offset).
- Transmitted shadows: two texels on the receiver, two opaque-map texels
  for the caster's hidden-glass test; the draw uniforms are now visible
  to the fragment stage for that.
- Virtual shadow maps: one virtual texel in the scene helper, two in the
  material helper.

The hardware GI variants trace their sun visibility and are unchanged.

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

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Shadow sampling paths replace fixed depth-bias values with calculations based on shadow texel size and projection scale. The changes also add receiver normal offsets to selected cascade sampling paths and derive transmitted-shadow blocker tolerance from map dimensions.

Changes: Shadow Bias Scaling

Layer / File(s) Summary
Virtual shadow sampling
native/shared/shaders/virtual_shadows/*
Virtual shadow helpers derive receiver depth bias from the cascade matrix and virtual-page dimensions instead of subtracting a fixed NDC value.
Environment and path-traced cascade sampling
native/shared/src/renderer/shaders/env.rs, native/shared/src/renderer/shaders/pt.rs
Environment and path-traced cascade sampling derive receiver bias from projection scale and texel size. Path-traced sampling offsets the receiver along its normal; debug mode 18 checks visibility at the unoffset surface.
GI shadow sampling and bias parameters
native/shared/src/renderer/shaders/gi.rs, native/shared/src/renderer/gi_bake.rs, native/shared/src/renderer/mod.rs, native/shared/src/renderer/types.rs
Card-light and WSRC shaders convert texel-count bias to NDC depth units. Card-light cascade candidates use a 1.5-texel normal offset. The configured bias values and parameter documentation use shadow texels.
Transmitted shadow tolerances
native/shared/src/renderer/transmitted_shadows.rs
The caster derives blocker tolerance from the light matrix and opaque-map width. Visibility derives receiver bias from the cascade matrix and transmitted-map width. The draw uniform is available to both vertex and fragment stages.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: Minimal · up to 204ea

This change makes shadow receiver bias scale with world units across several shadow readers. No concrete merge-blocking defect was found, and the author reports tests, with no golden image changes.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the main change: shadow receiver bias is now measured in world units for every directional cascade reader. The changeset matches this. It replaces fixed NDC biases with texel-based wor…
Description check Passed The description covers the summary, the affected readers, and the exceptions. It includes a before-and-after table, the test results, and a note on the known failing test. It follows the template's Su…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: shadow receiver bias is now measured in world units for every directional cascade reader. The changeset matches this. It replaces fixed NDC biases with texel-based world-sized biases across GI card relight, WSRC bake, path-tracer sun visibility, planar reflections, transmitted shadows, and virtual shadow maps. The title is specific and does not use vague terms. It does not mention that the change is a follow-up to #199 or that it leaves hardware GI and perspective-depth biases unchanged, but it does not need to cover every detail.

Full details: Description check

Explanation

The description covers the summary, the affected readers, and the exceptions. It includes a before-and-after table, the test results, and a note on the known failing test. It follows the template's Summary and Test plan sections. It has several gaps. The template's checklist items are not all addressed: it does not state whether the change was tested on a second platform, whether docs were updated for the public API, or whether the change was run through ./scripts/ci-check.sh, although the author ran a quick variant. The Notes for reviewers section is not present as a heading, though the failing test and the unchanged hardware variants are described in the body. The CI box is unchecked. These gaps are non-critical, so the description is mostly complete.

Finishing Touches

Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d056e906-f652-43a4-8d49-f39d15d581df
Commits

Reviewing files that changed from the base of the PR and between 49cf675 and 204eac2.

Files selected for processing: 9

  • native/shared/shaders/virtual_shadows/material_helper.wgsl
  • native/shared/shaders/virtual_shadows/scene_helper.wgsl
  • native/shared/src/renderer/gi_bake.rs
  • native/shared/src/renderer/mod.rs
  • native/shared/src/renderer/shaders/env.rs
  • native/shared/src/renderer/shaders/gi.rs
  • native/shared/src/renderer/shaders/pt.rs
  • native/shared/src/renderer/transmitted_shadows.rs
  • native/shared/src/renderer/types.rs

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

@proggeramlug
proggeramlug merged commit 74ff867 into main Nov 12, 2026
26 checks passed
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