How to force TSL shader recompile?

I’m trying to add light.shadow.blurSamples support to PCFShadowMap. I want to use the light.shadow.blurSamples value in a TSL shader, but only as a JS value (for loop unrolling), and not as a value in the built shader.

You can see in this snippet I’m using the input shadow.blurSamples, where shadow is the light.shadow (f.e. SpotLightShadow):

export const PCFShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord, shadow, depthLayer } ) => {

	// ... other code ...

	const samples = [];

	for ( let i = 0, l = shadow.blurSamples; i < l; i ++ ) {

		samples.push(

			depthCompare( shadowCoord.xy.add( vogelDiskSample( i, shadow.blurSamples, phi ).mul( radiusScaled ) ), shadowCoord.z ),

		);

	}

	return add( ...samples ).mul( 1 / shadow.blurSamples );

} );

Here I try to add a reference(), but that doesn’t work because the node is not connected to anything:

export const PCFShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord, shadow, depthLayer } ) => {

	// ... other code here ...

	// I hoped this might work (but of course not, it is unused):
	const blurSamples = reference( 'blurSamples', 'int', shadow ).setGroup( renderGroup );

	const samples = [];

	// ... the same ...

} );

If I update a light’s shadow.blurSamples, nothing happens. The TSL shader is built only once, initially.

If I change it to a shader-side loop (which makes the shader much slower), then updating blurSamples works:

export const PCFShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord, shadow, depthLayer } ) => {

	// ... other code here ...

	const blurSamples = reference( 'blurSamples', 'int', shadow ).setGroup( renderGroup );

	let sum = float( 0 ).toVar();

	Loop( blurSamples, ( { i } ) => { // too slow! But it updates.

		sum.addAssign( depthCompare( shadowCoord.xy.add( vogelDiskSample( i, shadow.blurSamples, phi ).mul( radiusScaled ) ), shadowCoord.z ) );

	} );

	return sum.mul( float( 1 ).div( float( blurSamples ) ) );

} );

How do we make the TSL shader update?

I tried:

light.shadow.blurSamples = newValue

light.shadow.needsUpdate = true // did not work, nothing happens

The following also does not work, and the rendering freezes:

light.shadow.blurSamples = newValue

light.shadow.dispose() // did not work, rendering freezes.

I have never played with TSL shadows. A JavaScript for loop should appear as unrolled loop in a shader. I’m not sure why a shader Loop loop would be slower – I mean it could be slower, but I’d expect the slowness to be negligible compared to unrolled loop.

Do you have a minimal example I could try? Most likely I will not find anything, but I’m just curious.

AFAIR I’ve read someone answering someone else how to force a Node-based shader to recompile, and it was some method, but this is very vague recollection and potentially inaccurate.

1 Like

I’m trying to make a reproduction I can easily share via jsfiddle, but it’s tricky to patch a module export such as export PCFShadowFilter.

Here’s a fiddle that seems to patch the module via importmap, but for some reason (in macOS Safari) the number of passes seems to have no effect, which makes it seems like my patch isn’t the one being used, see the HTML for the importmap patch:

If we can get the patch concept working, then we can patch anything in Three.js via importmaps on jsfiddle to easily share such ideas.