Skip to content

Prevent division by zero in CAS sharpening shader#8532

Merged
slimbuck merged 2 commits intoplaycanvas:mainfrom
slimbuck:cas-dev
Mar 16, 2026
Merged

Prevent division by zero in CAS sharpening shader#8532
slimbuck merged 2 commits intoplaycanvas:mainfrom
slimbuck:cas-dev

Conversation

@slimbuck
Copy link
Member

Summary

  • Fix division by zero in the Contrast Adaptive Sharpening (CAS) shader that causes pixels to render as black
  • Guard sharpening_amount calculation against max_g == 0, which occurs when all samples in the cross pattern have zero green channel (black regions, pure red/blue areas, uninitialized textures)
  • Guard toHDR reverse tone mapping against maxComponent == 1.0 to prevent a secondary division by zero

Details

The CAS algorithm computes sharpening_amount = sqrt(min(1.0 - max_g, min_g) / max_g). When max_g is zero, this produces NaN, which propagates through the entire color calculation. GPUs render NaN as black, causing visible black artifacts wherever pixel neighborhoods lack green channel intensity.

The fix applies to both GLSL (WebGL) and WGSL (WebGPU) shader variants:

// Before
float sharpening_amount = sqrt(min(1.0 - max_g, min_g) / max_g);

// After
float sharpening_amount = max_g > 0.0 ? sqrt(min(1.0 - max_g, min_g) / max_g) : 0.0;

When max_g is zero there is no contrast to sharpen, so returning 0 is the correct behavior — the pixel passes through unmodified.

@slimbuck slimbuck requested review from a team and Copilot March 16, 2026 13:37
@slimbuck slimbuck self-assigned this Mar 16, 2026
@slimbuck slimbuck added the area: graphics Graphics related issue label Mar 16, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the Contrast Adaptive Sharpening (CAS) shader chunks to be more numerically robust in HDR mode by preventing problematic divisions during tone-map inversion and sharpening weight computation.

Changes:

  • Clamp the toHDR denominator to avoid near-zero (or zero) divisions when converting SDR back to HDR.
  • Add a zero-guard around the sharpening amount computation to avoid dividing by max_g == 0.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/scene/shader-lib/wgsl/chunks/render-pass/frag/compose/compose-cas.js Adds denominator clamp for HDR inversion and attempts to guard sharpening calculation in WGSL.
src/scene/shader-lib/glsl/chunks/render-pass/frag/compose/compose-cas.js Adds denominator clamp for HDR inversion and guards sharpening calculation using a ternary in GLSL.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@slimbuck slimbuck merged commit 984e55f into playcanvas:main Mar 16, 2026
6 of 8 checks passed
@slimbuck slimbuck deleted the cas-dev branch March 16, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: graphics Graphics related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants