Sign in to follow this  
Silverlan

Issue using 2D depth texture array and regular depth texture in same shader

Recommended Posts

Silverlan    662

I'm using a texture array for cascaded shadow maps, and regular depth textures for spot-lights. They work fine, as long as I don't have them active at the same time in my shader.

In fact, after some testing, I found out that as soon as I bind ANY other depth texture to the shader, the shadows from the texture array are rendered incorrectly.

Here are the relevant snippets from the shaders / code:

Generating the shadow texture array:

unsigned int frameBuffer;
glGenFramebuffers(1,&frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER,frameBuffer);
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D_ARRAY,texture);
 
glTexImage3D(
    GL_TEXTURE_2D_ARRAY,0,GL_DEPTH_COMPONENT24,
    size,size,GetSplitCount(),1,GL_DEPTH_COMPONENT,GL_FLOAT,NULL
);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_COMPARE_MODE,GL_COMPARE_R_TO_TEXTURE);
 
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER);
glFramebufferTextureLayer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,texture,0,0);

The fragment shader function for the texture lookup:

float CalculateShadowTerm(sampler2DArrayShadow shadowMap,vec4 worldCoord,float bias)
{
    int index = numCascades -1;
    mat4 vp;
    for(int i=0;i<numCascades;i++)
    {
        if(gl_FragCoord.z < csmFard[i])
        {
            vp = csmVP[i];
            index = i;
            break;
        }
    }
    vec4 shadowCoord = vp *worldCoord;
    shadowCoord.w = shadowCoord.z *0.5f +0.5f -bias;
    shadowCoord.z = float(index);
    shadowCoord.x = shadowCoord.x *0.5f +0.5f;
    shadowCoord.y = shadowCoord.y *0.5f +0.5f;
 
    return shadow2DArray(shadowMap,shadowCoord +vec4(
        offset.x *shadowRatioX *shadowCoord.w,
        offset.y *shadowRatioY *shadowCoord.w,
        0.0,
        0.0
    )).x;
}
 

The result is essentially just multiplied with the output color.

This is working as it should (At the top in the image are the contents of the 4 cascades):

http://puu.sh/f1p23/d9f5549847.jpg

However, as soon as I add any texture look-up to a different depth texture anywhere in the fragment shader, it suddenly gets rendered incorrectly.

Here's the second depth texture for testing purposes:

unsigned int frameBuffer;
glGenFramebuffers(1,&frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER,frameBuffer);
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D,texture);
glTexImage2D(
    GL_TEXTURE_2D,0,
    GL_DEPTH_COMPONENT16,
    128,128,0,
    GL_DEPTH_COMPONENT,GL_FLOAT,(void*)0
);
 
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,0);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,texture,0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
 
Fragment Shader:
uniform sampler2D testShadowMap;
void main()
{
    texture(testShadowMap,vec2(0,0));
    [...]
}

This shouldn't do anything whatsoever, and they're bound to different texture units, however suddenly the result is this:

http://puu.sh/f1p9R/92e86e22b7.jpg

Each cascade is now rendered with different intensity, even though the actual contents in the depth textures is the same as before.

It looks like 'shadow2DArray' suddenly doesn't seem to do the actual depth comparison anymore and just returns the actual depth value (Value between 0 and 1).

If I bind any non-depth texture instead of the test-texture, or if I just comment out the texture lookup ('testShadowMap'), it works again.

What's going on here?

 

Share this post


Link to post
Share on other sites
hannesp    579

For me it seems that something with the sampler bindings is not how you think it is. When you get changes by commenting out something arbitrary and it works, most likely you do a binding with this call, that does affect the gl state where you don't expect it. Could you make sure that your two textures are bound to different texture units? Or could you post your code where you actually do the binding? Do you bind samplers as well? Because I can't find any "glActiveTexture" statements in your code.

Share this post


Link to post
Share on other sites
Silverlan    662

For me it seems that something with the sampler bindings is not how you think it is. When you get changes by commenting out something arbitrary and it works, most likely you do a binding with this call, that does affect the gl state where you don't expect it. Could you make sure that your two textures are bound to different texture units? Or could you post your code where you actually do the binding? Do you bind samplers as well? Because I can't find any "glActiveTexture" statements in your code.

 

Thanks for the response.

As I've said, they're bound to different texture units:

 

Spotlight:

glActiveTexture(GL_TEXTURE0 +SHADER_SHADOW_MAP_TEXTURE_START +idxSpot);
glBindTexture(GL_TEXTURE_2D,shadow->GetDepthTexture());
glActiveTexture(GL_TEXTURE0);
idxSpot++;

Directional light:

glActiveTexture(GL_TEXTURE0 +SHADER_SHADOW_MAP_TEXTURE_START +idxDirectional +CGame::MAX_ACTIVE_LIGHTS_PER_ENTITY *2);
glBindTexture(GL_TEXTURE_2D_ARRAY,shadow->GetDepthTexture());
glActiveTexture(GL_TEXTURE0);
idxDirectional++;

'SHADER_SHADOW_MAP_TEXTURE_START' is a constant set to 20. Since there's only one spotlight and one directional light in the example scene, 'idxSpot' and 'idxDirectional' are both 0. 'MAX_ACTIVE_LIGHTS_PER_ENTITY' is a constant set to 8.

So, in essence, the texture unit for the spotlight shadow is (GL_TEXTURE0 +20) and for the directional shadow it's (GL_TEXTURE0 +28).

 

I'm not using any samplers either.

Share this post


Link to post
Share on other sites
hannesp    579

Ok, seems to be okay, I just wanted to be sure.

 

And you evaluate all lights in a single draw call (forward rendering), so you use your texture array and the single depth texture in the same fragment shader? What exactly are you doing with the sample from the single depth texture? Because in your code snippet, you don't use the sample, so the compiler will most likely throw this statement away. If possible, just post the complete fragment shader code - maybe then, things will clear up - or else I'm afraid I cant find your mistake either :)

Share this post


Link to post
Share on other sites
Silverlan    662

And you evaluate all lights in a single draw call (forward rendering), so you use your texture array and the single depth texture in the same fragment shader? What exactly are you doing with the sample from the single depth texture? Because in your code snippet, you don't use the sample, so the compiler will most likely throw this statement away. If possible, just post the complete fragment shader code - maybe then, things will clear up - or else I'm afraid I cant find your mistake either smile.png

 

I trimmed the fragment shader down to the essential parts (Which still cause the issue):

http://pastebin.com/QcQnBtpa

 

This is the result if 'SPOTLIGHT_TEST' is set to '0' (Spotlight deactivated):

http://puu.sh/fpqij/7eeeff5906.jpg

 

This is the result if 'SPOTLIGHT_TEST' is set to '1' (Spotlight activated):

http://puu.sh/fpqgP/8bf754dce2.jpg

 

This is the result if 'SPOTLIGHT_TEST' is set to '2' (Spotlight activated, but the result of the shadow lookup is ignored):

http://puu.sh/fpqtd/d7ea652d78.jpg

 

Basically, as soon as there's any lookup for the shadow map of the spotlight, the issue occurs, even if the result is not used at all.

Share this post


Link to post
Share on other sites
hannesp    579

Puh, I tried to find any mistake for half an hour, but it should work as it is... so I have to continue with stupid questions:

 

Can you remove the spot light shadow map sample completely and replace it with a hardcoded 0.5 or something, so that we can be sure you don't have a sampling instruction in your compiled shader? When you have the spotlight activated (but now without shadows), does it work?

 

I hope at least this works - if not, could you try to rename one of your shadow-sample-methods, so that is doesn't use overloaded methods any more?

 

Sorry for my stupid assumptions, but in the past, I had so many unbelievable stupid compile- and runtime errors with opengl.

Share this post


Link to post
Share on other sites
Silverlan    662

Can you remove the spot light shadow map sample completely and replace it with a hardcoded 0.5 or something, so that we can be sure you don't have a sampling instruction in your compiled shader? When you have the spotlight activated (but now without shadows), does it work?

All I have to do to make it work is remove the lookup into the shadow map ('shadow2DProj').

As soon as there's ANY lookup into the shadow map, the problem occurs. I've tried other lookup-functions instead of 'shadow2DProj' (e.g. 'texture2D'), however that didn't help either.

The shadow map itself is not at fault either, it's the same if I use any other depth texture. If I use an additional RGB texture instead, the problem doesn't occur.

 

I hope at least this works - if not, could you try to rename one of your shadow-sample-methods, so that is doesn't use overloaded methods any more?

I tried renaming one of the functions, but the result is still the same.

Share this post


Link to post
Share on other sites
Silverlan    662

err... update drivers?

My drivers are up to date and my graphics card isn't that old either (AMD Radeon HD 7870).

 

I did make some progress however.

I basically ended up binding the shadow map of my spotlight to ALL samplers in the shadow map array ('shadowMaps'), and now it works as it should:

http://puu.sh/ft0N9/8cab0af7a7.jpg

 

If just one texture in the array (Regardless of whether the texture is actually being used) is bound to an invalid texture (e.g. '0'), the problem occurs.

 

So the problem seems to be fixed now, but I don't really understand what's going on here. Is this a hardware-dependent thing, or part of the OpenGL specification?

Share this post


Link to post
Share on other sites
hannesp    579

Sorry I'm afraid I'm not able to follow you. What do you mean by "binding the shadow map of my spotlight to ALL samplers in the shadow map array" ? I thought you didn't use texture arrays for your spotlight depth maps? You have to bind your array texture from your cascaded shadow map for your directional light and then, you bind some regular textures to some other units, you got that, or did I miss the point?

Share this post


Link to post
Share on other sites
Silverlan    662

Sorry I'm afraid I'm not able to follow you. What do you mean by "binding the shadow map of my spotlight to ALL samplers in the shadow map array" ? I thought you didn't use texture arrays for your spotlight depth maps? You have to bind your array texture from your cascaded shadow map for your directional light and then, you bind some regular textures to some other units, you got that, or did I miss the point?

Sorry, I think I was using the wrong terminology there.

I meant to say "array of samplers", not "shadow map array".

In essence:

  • The CSM shadow map is a 2D texture array
  • The spotlight shadow maps are regular 2D depth textures (A separate texture for each spotlight)
  • The CSM shadow is used in the shader as a sampler2DArrayShadow ("csmTextureArray")
  • The spotlight shadow maps are used in the shader as an array of sampler2DShadows ("shadowMaps[MAX_LIGHTS]")

During the actual rendering, I bound the CSM texture to 'csmTextureArray', and the spotlight shadow texture to 'shadowMaps[0]'.

The problem seems to be that ALL textures inside the array ('shadowMaps[MAX_LIGHTS]') need to be bound to a valid depth texture, not just the ones that are actually being used in the shader. So I ended up binding the spotlight shadow texture to not just 'shadowMaps[0]', but to ALL of the locations (0 to MAX_LIGHTS).

Share this post


Link to post
Share on other sites
hannesp    579

 

 

  • The spotlight shadow maps are regular 2D depth textures (A separate texture for each spotlight)
  • ...
  • The spotlight shadow maps are used in the shader as an array of sampler2DShadows ("shadowMaps[MAX_LIGHTS]")

 

Using an array of textures is not nearly the same as using an array texture. Of course, you have to bind every of your depth maps for itself to its own sampler. That's the whole point of array textures, because only with them, you don't need to do this. As I said in my last post.

Share this post


Link to post
Share on other sites
Silverlan    662

 

 

 

  • The spotlight shadow maps are regular 2D depth textures (A separate texture for each spotlight)
  • ...
  • The spotlight shadow maps are used in the shader as an array of sampler2DShadows ("shadowMaps[MAX_LIGHTS]")

 

Using an array of textures is not nearly the same as using an array texture. Of course, you have to bind every of your depth maps for itself to its own sampler. That's the whole point of array textures, because only with them, you don't need to do this. As I said in my last post.

 

 

All samplers in the 'shadowMaps'-array have different texture units bound to them, e.g.:

shadowMaps[0] = Texture Unit 21; (GL_TEXTURE_20)

shadowMaps[1] = Texture Unit 22; (GL_TEXTURE_21)

etc.

 

The spotlight shadow maps are then bound to the texture units the same way:

spotLightShadow[0] -> GL_TEXTURE_20

spotLightShadow[1] -> GL_TEXTURE_21

etc.

 

Now let's say there are only two spotlights. This means that no valid texture is bound to texture unit GL_TEXTURE_22, and thus shadowMaps[2].

This shouldn't be a problem, because shadowMaps[2] isn't being used in the shader, but it causes the issues I've been talking about.

This is what I meant by 'ALL textures inside the array need to be bound to a valid depth texture'.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this