@@ -188,11 +188,11 @@ vec2 pcssVogelDiskSample(int sampleIndex, int sampleCount, float angle) {
188188 return vec2(cosine, sine) * r;
189189}
190190
191- float penumbraSize ( const in float zReceiver, const in float zBlocker ) {
191+ float pcssPenumbraSize ( const in float zReceiver, const in float zBlocker ) {
192192 return (zReceiver - zBlocker) / zBlocker;
193193}
194194
195- float findBlocker (sampler2D shadowMap, vec2 uv, float compare, float angle) {
195+ float pcssFindBlocker (sampler2D shadowMap, vec2 uv, float compare, float angle) {
196196 float texelSize = 1.0 / float(textureSize(shadowMap, 0).x);
197197 float blockerDepthSum = float(${ focus } );
198198 float blockers = 0.0;
@@ -241,32 +241,16 @@ float PCSS (sampler2D shadowMap, vec4 coords, float shadowIntensity) {
241241 vec2 uv = coords.xy;
242242 float zReceiver = coords.z;
243243 float angle = highPassRandRGB(gl_FragCoord.xy).r * PI2;
244- float avgBlockerDepth = findBlocker (shadowMap, uv, zReceiver, angle);
244+ float avgBlockerDepth = pcssFindBlocker (shadowMap, uv, zReceiver, angle);
245245 if (avgBlockerDepth == -1.0) {
246246 return 1.0;
247247 }
248- float penumbraRatio = penumbraSize (zReceiver, avgBlockerDepth);
248+ float penumbraRatio = pcssPenumbraSize (zReceiver, avgBlockerDepth);
249249 float shadow = pcssVogelFilter(shadowMap, uv, zReceiver, 1.25 * penumbraRatio, angle);
250250 return mix( 1.0, shadow, shadowIntensity );
251251}` ;
252252}
253253
254- /**
255- * Generates the replacement getShadow function for r182+
256- */
257- function getShadowReplacement ( ) : string {
258- return `float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
259- shadowCoord.xyz /= shadowCoord.w;
260- shadowCoord.z += shadowBias;
261- bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
262- bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
263- if ( frustumTest ) {
264- return PCSS( shadowMap, shadowCoord, shadowIntensity );
265- }
266- return 1.0;
267- }` ;
268- }
269-
270254function reset ( gl : THREE . WebGLRenderer , scene : THREE . Scene , camera : THREE . Camera ) : void {
271255 scene . traverse ( ( object ) => {
272256 if ( ( object as THREE . Mesh ) . material ) {
@@ -304,17 +288,52 @@ export class NgtsSoftShadows {
304288 const original = THREE . ShaderChunk . shadowmap_pars_fragment ;
305289
306290 if ( version >= 182 ) {
307- // Three.js r182+ uses native depth textures and has a different shader structure
308- // We need to replace the getShadow function entirely
291+ // Three.js r182+ uses native depth textures and has a different shader structure.
292+ // The PCF path uses sampler2DShadow, but PCSS needs sampler2D for manual depth comparison.
293+ // We inject our PCSS code and replace the BASIC shadow type's getShadow function,
294+ // then also replace the PCF uniform declarations to use sampler2D instead of sampler2DShadow.
309295 const pcssCode = pcssModern ( options ) ;
310296
311- // Find and replace the PCF getShadow function
312- const getShadowRegex =
313- / ( # i f d e f i n e d \( S H A D O W M A P _ T Y P E _ P C F \) \s + f l o a t g e t S h a d o w \( s a m p l e r 2 D S h a d o w s h a d o w M a p [ ^ } ] + \} ) / s;
297+ let shader = THREE . ShaderChunk . shadowmap_pars_fragment ;
298+
299+ // 1. Inject PCSS functions after USE_SHADOWMAP
300+ shader = shader . replace ( '#ifdef USE_SHADOWMAP' , '#ifdef USE_SHADOWMAP\n' + pcssCode ) ;
301+
302+ // 2. Replace sampler2DShadow with sampler2D for directional lights (PCF path)
303+ shader = shader . replace (
304+ / # i f d e f i n e d \( S H A D O W M A P _ T Y P E _ P C F \) \s + u n i f o r m s a m p l e r 2 D S h a d o w d i r e c t i o n a l S h a d o w M a p \[ N U M _ D I R _ L I G H T _ S H A D O W S \] ; / ,
305+ `#if defined( SHADOWMAP_TYPE_PCF )
306+ uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];` ,
307+ ) ;
308+
309+ // 3. Replace sampler2DShadow with sampler2D for spot lights (PCF path)
310+ shader = shader . replace (
311+ / # i f d e f i n e d \( S H A D O W M A P _ T Y P E _ P C F \) \s + u n i f o r m s a m p l e r 2 D S h a d o w s p o t S h a d o w M a p \[ N U M _ S P O T _ L I G H T _ S H A D O W S \] ; / ,
312+ `#if defined( SHADOWMAP_TYPE_PCF )
313+ uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];` ,
314+ ) ;
315+
316+ // 4. Replace the PCF getShadow function to use our PCSS
317+ // Match from the function signature to its closing brace
318+ const getShadowPCFRegex =
319+ / ( # i f d e f i n e d \( S H A D O W M A P _ T Y P E _ P C F \) \s + f l o a t g e t S h a d o w \( s a m p l e r 2 D S h a d o w s h a d o w M a p , v e c 2 s h a d o w M a p S i z e , f l o a t s h a d o w I n t e n s i t y , f l o a t s h a d o w B i a s , f l o a t s h a d o w R a d i u s , v e c 4 s h a d o w C o o r d \) \{ [ \s \S ] * ?r e t u r n m i x \( 1 \. 0 , s h a d o w , s h a d o w I n t e n s i t y \) ; \s * \} ) / ;
320+
321+ shader = shader . replace (
322+ getShadowPCFRegex ,
323+ `#if defined( SHADOWMAP_TYPE_PCF )
324+ float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
325+ shadowCoord.xyz /= shadowCoord.w;
326+ shadowCoord.z += shadowBias;
327+ bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
328+ bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
329+ if ( frustumTest ) {
330+ return PCSS( shadowMap, shadowCoord, shadowIntensity );
331+ }
332+ return 1.0;
333+ }` ,
334+ ) ;
314335
315- THREE . ShaderChunk . shadowmap_pars_fragment = THREE . ShaderChunk . shadowmap_pars_fragment
316- . replace ( '#ifdef USE_SHADOWMAP' , '#ifdef USE_SHADOWMAP\n' + pcssCode )
317- . replace ( getShadowRegex , `#if defined( SHADOWMAP_TYPE_PCF )\n\t\t${ getShadowReplacement ( ) } ` ) ;
336+ THREE . ShaderChunk . shadowmap_pars_fragment = shader ;
318337 } else {
319338 // Three.js < r182 uses RGBA-packed depth
320339 THREE . ShaderChunk . shadowmap_pars_fragment = THREE . ShaderChunk . shadowmap_pars_fragment
0 commit comments