Skip to content

[Draft/RFC] Add angular ray bias to ContactShadows to fix steep slope artifacts - #25355

Open
Affinator wants to merge 1 commit into
bevyengine:mainfrom
Affinator:contact_shadows_normal_bias
Open

[Draft/RFC] Add angular ray bias to ContactShadows to fix steep slope artifacts #25355
Affinator wants to merge 1 commit into
bevyengine:mainfrom
Affinator:contact_shadows_normal_bias

Conversation

@Affinator

Copy link
Copy Markdown

Full disclosure: This PR description has been generated with AI. The code was written by hand.

Objective

  • Screen-space contact shadows currently suffer from severe self-shadowing artifacts on steep slopes and terrain.
  • Because the ray marches strictly in screen space against the 2.5D depth buffer, rays originating on steep slopes frequently intersect the "staircase" representation of the surface almost immediately, causing large false shadows.
  • This is an RFC/Draft PR to discuss a potential solution to this visual artifact.

Solution

  • The shader now dynamically bends the contact shadow ray away from the surface (towards the normal) by up to ray_bias_degrees. The geometric approach is as follows:

    1. We evaluate the angle between the light direction and the surface normal (NdotL).
    2. If the light hits the surface at a steep grazing angle (e.g., NdotL < 0.25), the bias applies fully. As the angle becomes more direct (NdotL approaches 0.45), the bias smoothly fades to zero via a smoothstep curve.
    3. We calculate a rotation angle theta (in radians) by multiplying ray_bias_degrees by this fade factor and converting to rad.
    4. We construct an orthonormal basis from the normal and the light direction, finding the tangent vector T that points directly away from the surface.
    5. We rotate the original light direction vector towards the normal by theta using light_dir * cos(theta) + T * sin(theta).
    6. The contact shadow is then marched along this new "bent" vector. Because it travels at a slightly higher trajectory relative to the surface, it safely clears the depth buffer's jagged "staircase" representation without prematurely intersecting the floor.
  • While we experimented with more classical shadow normal biases (like shifting the ray's origin or target), they introduced other forms of artifacts. This angular ray bending solution clears the depth buffer's staircase artifacts and creates very believable contact shadows, which are only slightly "physically wrong" in these specific grazing angle ranges.

  • Added an early-exit optimization: We now immediately exit and return no shadow if the surface is facing away from the light (NdotL < 0.0), since the surface is self-occluding by definition and handled by macro shadow maps. This is not necessary to fix the artifacts, but since we now have this information available, this seems to be a sensible optimization.

Visual Proof

Before (ray_bias_degrees = 0.0) After (ray_bias_degrees = 6.5)

|
before
|
after
|


Changelog

  • Added ray_bias_degrees to ContactShadows and its corresponding uniform/WESL types to control angular ray bending at grazing angles.
  • Removed the @if(SIXTEEN_BYTE_ALIGNMENT) _padding: f32 in the ContactShadowsSettings WESL struct, as the addition of ray_bias_degrees naturally aligns the struct to 16 bytes.

Migration Guide

  • ContactShadows has a new ray_bias_degrees field. If you were manually constructing ContactShadows without ..default(), you will need to add ray_bias_degrees (e.g. 6.5 or lower depending on your scene) to your struct instantiation.

…surface normal on steep surfaces (prevents self occlusion)
@github-actions

Copy link
Copy Markdown
Contributor

Welcome, new contributor!

Please make sure you've read our contributing guide, as well as our policy regarding AI usage, and we look forward to reviewing your pull request shortly ✨

@JaySpruce JaySpruce added A-Rendering Drawing game state to the screen S-Needs-Review Needs reviewer attention (from anyone!) to move forward D-Shaders This code uses GPU shader languages C-Refinement Improves output quality, without fixing a clear bug or adding new functionality. labels Aug 10, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in Rendering Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Refinement Improves output quality, without fixing a clear bug or adding new functionality. D-Shaders This code uses GPU shader languages S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

2 participants