From 8de29f95c9a789a3e05227be805ef6836e8d867b Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Wed, 5 Nov 2025 00:03:47 +0900 Subject: [PATCH] WebGLRenderer: Optimize VSM shadow code. --- .../shadowmap_pars_fragment.glsl.js | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js index fc8ba6af766c91..01bb641c4c8978 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js @@ -89,30 +89,37 @@ export default /* glsl */` float VSMShadow( sampler2D shadow, vec2 uv, float compare ) { - float occlusion = 1.0; - vec2 distribution = texture2DDistribution( shadow, uv ); + float mean = distribution.x; + float variance = distribution.y * distribution.y; + #ifdef USE_REVERSED_DEPTH_BUFFER - float hard_shadow = step( distribution.x, compare ); + float hard_shadow = step( mean, compare ); #else - float hard_shadow = step( compare, distribution.x ); + float hard_shadow = step( compare, mean ); #endif - if ( hard_shadow != 1.0 ) { + // Early return if fully lit + if ( hard_shadow == 1.0 ) return 1.0; - float distance = compare - distribution.x; - float variance = max( 0.00000, distribution.y * distribution.y ); - float softness_probability = variance / (variance + distance * distance ); // Chebeyshevs inequality - softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); // 0.3 reduces light bleed - occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); + // Variance must be non-zero to avoid division by zero + variance = max( variance, 0.0000001 ); - } - return occlusion; + // Distance from mean + float d = compare - mean; + + // Chebyshev's inequality for upper bound on probability + float p_max = variance / ( variance + d * d ); + + // Reduce light bleeding by remapping [amount, 1] to [0, 1] + p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 ); + + return max( hard_shadow, p_max ); }