@@ -89,30 +89,37 @@ export default /* glsl */`
8989
9090 float VSMShadow( sampler2D shadow, vec2 uv, float compare ) {
9191
92- float occlusion = 1.0;
93-
9492 vec2 distribution = texture2DDistribution( shadow, uv );
9593
94+ float mean = distribution.x;
95+ float variance = distribution.y * distribution.y;
96+
9697 #ifdef USE_REVERSED_DEPTH_BUFFER
9798
98- float hard_shadow = step( distribution.x , compare );
99+ float hard_shadow = step( mean , compare );
99100
100101 #else
101102
102- float hard_shadow = step( compare, distribution.x );
103+ float hard_shadow = step( compare, mean );
103104
104105 #endif
105106
106- if ( hard_shadow != 1.0 ) {
107+ // Early return if fully lit
108+ if ( hard_shadow == 1.0 ) return 1.0;
107109
108- float distance = compare - distribution.x;
109- float variance = max( 0.00000, distribution.y * distribution.y );
110- float softness_probability = variance / (variance + distance * distance ); // Chebeyshevs inequality
111- softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); // 0.3 reduces light bleed
112- occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );
110+ // Variance must be non-zero to avoid division by zero
111+ variance = max( variance, 0.0000001 );
113112
114- }
115- return occlusion;
113+ // Distance from mean
114+ float d = compare - mean;
115+
116+ // Chebyshev's inequality for upper bound on probability
117+ float p_max = variance / ( variance + d * d );
118+
119+ // Reduce light bleeding by remapping [amount, 1] to [0, 1]
120+ p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 );
121+
122+ return max( hard_shadow, p_max );
116123
117124 }
118125
0 commit comments