cancel
Showing results for 
Search instead for 
Did you mean: 

OpenGL & Vulkan

joshklint
Adept II

Cascaded shadow map artifacts in GLSL

Our new renderer is displaying graphical artifacts in the cascaded shadow map implementation. This seems to occur near the dividing line between different shadow stages. It's almost as if the fragments are divided into regions that aren't completely independent from one another, and the shader logic gets confused if two neighboring fragments do a texture lookup on different textures. If I discard all but one stage, that stage will appear correctly with no artifacts.

GPU: Radeon 6600
Driver: adrenalin-edition-23.9.3
OS: Windows 10 64-bit
API: Vulkan 1.3

I will send the download link in a private message.

Untitled.png

Here's another shot in-editor with the stages colored:

Untitled.jpg.776bce31a8e4982f082bc9f75b5a4a44.jpg

The code that handles this at line 252 of "Source/Shaders/PBR/Lighting.glsl":

    case LIGHT_DIRECTIONAL:
#ifdef DOUBLE_FLOAT
        lightDir = vec3(normalize(lightmatrix[2].xyz));
#else
        lightDir = normalize(lightmatrix[2].xyz);
#endif
        if (dot(lightDir, normal) > 0.0f) return lighttype;
        vec3 camspacepos = (CameraInverseMatrix * vec4(position, 1.0)).xyz;
        mat4 shadowmat;
        visibility = 1.0f;
        if (camspacepos.z <= 80.0)
        {
            int index = 0;
            shadowmat = ExtractCameraProjectionMatrix(lightIndex, index);
            if (camspacepos.z > CameraRange.x + 10.0) index = 1;
            if (camspacepos.z > CameraRange.x + 20.0) index = 2;
            if (camspacepos.z > CameraRange.x + 40.0) index = 3;
            uint sublight = floatBitsToUint(shadowmat[0][index]);
            //shadowMapID = ExtractLightShadowMapIndex(sublight);
            mat4 shadowrendermatrix = ExtractLightShadowRenderMatrix(sublight);
            shadowCoord.xyz = (shadowrendermatrix * vec4(position, 1.0f)).xyz;
            shadowCoord.y *= -1.0f;
            ExtractLightInfo(sublight, coneangles, shadowkernel, shadowMapID);
            shadowCoord.xy /= coneangles;
            shadowCoord.xy += 0.5f;
            shadowrange = vec2(-500, 500);
            if (shadowCoord.x < 0.0f || shadowCoord.y < 0.0f || shadowCoord.x > 1.0f || shadowCoord.y > 1.0f) return lighttype;
#ifdef USE_VSM
            shadowCoord.z -= 0.05f;
            float samp = shadowSample( texture2DSampler[shadowMapID], shadowCoord.xyz, shadowrange, bias);
#else
            shadowCoord.z = (shadowCoord.z - shadowrange.x) / (shadowrange.y - shadowrange.x);
            float samp = shadowSample(texture2DShadowSampler[shadowMapID], shadowCoord.xyz, shadowkernel).r;
#endif
            if (camspacepos.z > CameraRange.x + 70.0)
            {
                samp = 1.0f - (1.0f - samp) * (1.0 - (camspacepos.z - 70.0) / 10.0);
            }
            visibility = samp;
            attenuation *= samp;
        }
        break;
0 Likes
1 Solution
joshklint
Adept II

Hi, I have an update on this issue.

The original problem was caused by the requirement for dynamically uniform expressions, which I did not fully understand at the time.

I did get the problem solved by merging all shadow maps into a single texture.

I was not ever able to get the nonuniformExt() feature to work. This might be due to my own error, or it might not be working on AMD drivers.

For my purposes, this problem is resolved.

View solution in original post

0 Likes
9 Replies

Hi @joshklint 

Does it occur in the other configs you have? I want to make sure we isolate this issue down to AMD specific hardware or driver bug rather than an implementation specific issue.

Thanks,

Owen

0 Likes
joshklint
Adept II

This artifact does not appear on my non-AMD hardware and has not been reported by any users with non-AMD hardware.

0 Likes

Created internal ticket SWDEV-426735 to track this issue.

Thanks,

Owen

0 Likes
BoyBaykiller
Adept II

This might be an artifact resulting from indexing and sampling textures using a non Dynamically uniform expression. AMD hardware needs the sample index to be the same for all active invocations inside the subgroup. 

One fix would be to do:

#extension GL_EXT_nonuniform_qualifier : require

textures[nonuniformEXT(index)]

So wrapping the texture index inside of the nonuniformEXT function which exist exactly for cases where you need non dynamically uniform ressource indexing.

Your description is a plausible explanation for this problem, but adding the extension did not have any effect on the appearance.

I changed this code:

#ifdef USE_VSM
            shadowCoord.z -= 0.05f;
            float samp = shadowSample( texture2DSampler[shadowMapID], shadowCoord.xyz, shadowrange, bias);
#else
            shadowCoord.z = (shadowCoord.z - shadowrange.x) / (shadowrange.y - shadowrange.x);
            float samp = shadowSample(texture2DShadowSampler[shadowMapID], shadowCoord.xyz, shadowkernel).r;
#endif
 
To this:
 
#ifdef USE_VSM
            shadowCoord.z -= 0.05f;
            float samp = shadowSample( texture2DSampler[nonuniformEXT(shadowMapID)], shadowCoord.xyz, shadowrange, bias);
#else
            shadowCoord.z = (shadowCoord.z - shadowrange.x) / (shadowrange.y - shadowrange.x);
            float samp = shadowSample(texture2DShadowSampler[nonuniformEXT(shadowMapID)], shadowCoord.xyz, shadowkernel).r;
#endif
 
I also had to add this value into the device features at startup to prevent a validation error:
VkPhysicalDeviceVulkan12Features.shaderSampledImageArrayNonUniformIndexing = true
 
However, this all has no apparent effect on the problem.
0 Likes

Ok, I've only ever used nonuniformEXT in OpenGL so I dont know what you need in Vulkan to properly use it, but seems like you got it. 

I wonder if the automatic derivatives could be an issue as well or if thats irrelevant when using shadow samplers.

You can try by sampling with textureGrad and just hardcoding the derivatives to 0.0, see if that does something.

0 Likes
joshklint
Adept II

I have updated the example to enable the following Vulkan 1.2 features:
physicalfeatures1_2.shaderSampledImageArrayNonUniformIndexing = VK_TRUE
physicalfeatures1_2.shaderStorageBufferArrayNonUniformIndexing = VK_TRUE
physicalfeatures1_2.shaderUniformBufferArrayNonUniformIndexing = VK_TRUE

The new EXE will show "Nonuniform indexing enabled" in the window title bar.

I tried putting nonuniformExt() around a lot of buffer and uniform indexes in the PBR/PBR.frag shader, but nothing I tried seemed to have any effect.

I am sending a new download link to Owen now.

0 Likes
joshklint
Adept II

Hi, I have an update on this issue.

The original problem was caused by the requirement for dynamically uniform expressions, which I did not fully understand at the time.

I did get the problem solved by merging all shadow maps into a single texture.

I was not ever able to get the nonuniformExt() feature to work. This might be due to my own error, or it might not be working on AMD drivers.

For my purposes, this problem is resolved.

0 Likes
joshklint
Adept II

I also want to add that our solution was implemented with OpenGL, but I saw a similar situation where the OpenGL driver did not exhibit the same issue with invocation groups. I have not actually verified that our solution solves the problem in Vulkan and do not plan to continue investigating this.

0 Likes