StrandedKitty / three-csm

☀️ Cascaded shadow maps (CSMs) implementation for Three.js
MIT License
296 stars 21 forks source link

THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false Program Info Log: ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 25 samplers #24

Closed itsdouges closed 1 year ago

itsdouges commented 1 year ago

Hello! I'm getting this error when rendering a scene that has custom models:

THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false

Program Info Log: ERROR: Implementation limit of 16 active fragment shader samplers (e.g., maximum number of supported image units) exceeded, fragment shader uses 25 samplers
image

If I strip out the custom models down to a single one they start working. Is there any information on how CSM works with GLSL shaders, am I missing an optimization step on my end to make it work? I don't have all the information currently so keen to learn a bit before digging it :)

StrandedKitty commented 1 year ago

CSM needs one sampler2D per cascade. It's basically the same as having multiple directional lights with shadowmapping enabled. I have no idea why there's so many samplers in your fragment shader, could you please share the compiled (with resolved includes) glsl code?

itsdouges commented 1 year ago

OK let me get it for you. As a side what's setupMaterial() function used for? Is it needed to be used or things can work without it? Currently I have a scene that works without it:

image

The shadows are a bit light however 🤔

And while I have you... is there a blur configuration available? It looks like when passing through the cascades you can see the split points:

image

Really appreciate your fast response BTW, very helpful 🙏

StrandedKitty commented 1 year ago

Calling setupMaterial applies some injections to glsl code so that shadows work correctly. With CSM there are multiple shadowmaps that cover different overlapping frustums, so in fragment shader it is required to select appropriate shadowmap for every pixel based on its distance to camera. This it what the injected code does. And without calling setupMaterial you end up fetching all your shadowmaps for every pixel.

Your issue is most likely related to Three.js library, not this plugin. You can try for example adding 3 (or whatever number of cascades you are using for CSM) directional lights with shadows into your scene. You should get the same error when compiling your shaders.

itsdouges commented 1 year ago

Calling setupMaterial applies some injections to glsl code so that shadows work correctly

Ah so in the example here you pass the same material that is used on the floor?

Is it undefined behaviour when not passing one? As it still seems to work. Edit:

And without calling setupMaterial you end up fetching all your shadowmaps for every pixel.

Nevermind you said what happens! Thank you! Does every material that is affected by shadows need to be passed through it?


As for this issue.. it looks like it was because (as I'm using React Three Fiber) CSM was being instantiated multiple times and not disposing itself thus resulting in a lot of extra shaders being compiled!

itsdouges commented 1 year ago

😄 Shadows are working either way

https://user-images.githubusercontent.com/6801309/201799704-4b40c791-1336-41c2-8153-3e347ab0ac8d.mov

itsdouges commented 1 year ago

What would be the best way to test that CSM cleans up all scene and shaders when calling dispose? If I make 100% sure it never needs to be disposed things work as expected, else it seems to keep creating new ones. If I were to get you the compiled shader code what material would I get it from?

Edit: Ah I think it's from https://github.com/StrandedKitty/three-csm/issues/25 actually.

StrandedKitty commented 1 year ago

Does every material that is affected by shadows need to be passed through it?

Yes.

As for this issue.. it looks like it was because (as I'm using React Three Fiber) CSM was being instantiated multiple times and not disposing itself thus resulting in a lot of extra shaders being compiled!

You can test it in 2.0.2. But I can't think of any case where it would be necessary to instantiate and delete CSM multiple times. You should instantiate it just once per every scene.

Regarding your video: it is possible that the first cascade is huge, and the next one is quite far away. So you only see the shadows produces by the first cascade. Also I don't really think there's a need for CSM in this specific scene, it looks quite small and it would probably be okay to just use a single directional light with shadows.

itsdouges commented 1 year ago

Thanks mate appreciate it! I'll take a look at what it would look like with a single directional light.

What would the recommendation be for using csm, how big would the scene have to be?

StrandedKitty commented 1 year ago

What would the recommendation be for using csm, how big would the scene have to be?

CSM is required if you want to have more detailed shadows near the camera, and less detailed far away. So typically it's used for landscape scenes, because in such cases it is impossible to have acceptable shadow quality near the camera with only one shadowmap. In your case I think having only one will be enough given that you position in properly. You can set CSM cascade count to 1 and set maxFar to a reasonable number so that this one cascade covers just enough space. This way you will save some shadowmapping drawcalls and have less samplers in your fragment shader.

itsdouges commented 1 year ago

CSM is required if you want to have more detailed shadows near the camera, and less detailed far away.

Sweet! That's exactly what I'm going for, for example I have this other scene I'm working on:

image

Thanks for all your help so far! I'll go ahead and close off this now, cheers.