Question

I am doing shadow mapping for spotlights in a deferred OpenGL 4.3 renderer.

I've been trying to follow some tutorials on the subject, and modelled by fragment shader after it, but what I do not understand is the final comparison to calculate the shadow factor. The values that I sample from the depth map ("unifShadowTexture") is in the range of [0, 1], since it is directly from the depth buffer but how is projCoords.z clamped to [0, 1]? Is the division by .w clamping it to [0,1]? The issues I am facing is that a major part of the scene is shadowed even though it should not be, such as the ground floor, in the following picture(ignore the light artifacts, due to the lack of bias - the important point is the models are lit, but the ground floor isnt):

enter image description here

const std::string gDirLightFragmentShader =
"#version 430                                                                                                                   \n \
                                                                                                                                \n \
layout(std140) uniform;                                                                                                         \n \
                                                                                                                                \n \
uniform UnifDirLight                                                                                                            \n \
{                                                                                                                               \n \
    mat4 mWVPMatrix;                                                                                                            \n \
    mat4 mVPMatrix;   // light view-projection matrix, pre-multiplied by the bias-matrix                                                                                                          \n \
    vec4 mLightColor;                                                                                                           \n \
    vec4 mLightDir;                                                                                                             \n \
    vec4 mGamma;                                                                                                                \n \
    vec2 mScreenSize;                                                                                                           \n \
} UnifDirLightPass;                                                                                                             \n \
                                                                                                                                \n \
layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                     \n \
layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                       \n \
layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                      \n \
layout (binding = 5) uniform sampler2D unifShadowTexture;                                                                       \n \
                                                                                                                                \n \
out vec4 fragColor;                                                                                                             \n \
                                                                                                                                \n \
void main()                                                                                                                     \n \
{                                                                                                                               \n \
    vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;                                                             \n \
                                                                                                                                \n \
    vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                 \n \
    vec3 normal   = normalize(texture(unifNormalTexture, texcoord).xyz);                                                        \n \
    vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                  \n \
                                                                                                                                \n \
    vec4 lightClipPos = UnifDirLightPass.mVPMatrix * vec4(worldPos, 1.0);                                                      \n \
    vec3 projCoords   = lightClipPos.xyz / lightClipPos.w;                                                                   \n \
                                                                                                                                \n \
    float depthValue = texture(unifShadowTexture, projCoords.xy).x;                                                           \n \
    float visibilty  = 1.0;                                                                                                   \n \
    if (depthValue < (projCoords.z))                                                                                  \n \
         visibilty = 0.0;                                                                                                    \n \
                                                                                                                                \n \
    float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);                                               \n \
                                                                                                                                \n \
    fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;                                 \n \
}                                                                                                                               \n";
Was it helpful?

Solution

The division by w by itself will not clamp it to [0,1]. Technically what you have are clip-space coordinates, and then division by w transforms them into NDC space. Any point x, y, or z that is < -w or > w is clipped when this operation is performed on geometry. However, when you do it on your texture coordinates you still need to provide an appropriate texture wrap mode (usually GL_CLAMP_TO_EDGE), because the coordinates are not clamped automatically.

Note that all points that project beyond your shadow map's far plane will exhibit the same behavior since the coordinates are clamped. Usually this manifests itself as everything beyond a certain distance being fully in shadow. This is why you want to match your shadow map's frustum more closely with your attenuated light volume.


Also, in this situation a sampler2DShadow makes a lot of sense...

This whole thing:

float depthValue = texture(unifShadowTexture, projCoords.xy).x;
float visibilty  = 1.0;

if (depthValue < (projCoords.z))
  visibilty = 0.0;            

Can be simplified to this:

float visibility = texture (unifShadowTexture, projCoords);

Provided you do two things:

  1. Declare unifShadowTexture as sampler2DShadow
  2. Set GL_TEXTURE_COMPARE_MODE to GL_COMPARE_R_TO_TEXTURE for your depth texture

This works because for sampler2DShadow, it requires 3D texture coordinates. s and t work as usual, and r is the value it uses for comparison. The default comparison function is GL_LESS, so you do not have to change anything in your code. Given a GL_NEAREST texture function, the return of this will be either 1.0 or 0.0. It will produce exactly the same as your code snippet above, but using a hardware feature for the actual depth test and will often improve performance and get you inexpensive PCF if you enable GL_LINEAR.

I woud suggest, however, that you add a bias to the Z component of your texture coordinates. Something like (0.001) or you will get a lot of "shadow acne", too high a bias and you will get "peter panning" (shadows seem to hover slightly above whatever they are attached to).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top