Game Development Stack Exchange is a question and answer site for professional and independent game developers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm implementing a shadow map for the sun light in my voxel engine using a depth texture attached to a framebuffer. But when I sample the depth texture during the normal pass, the value returned is always 0.

Here is the shadow map initialization :

    // Create and configure the depth texture
    shadowMapTextureId = GL11.glGenTextures();
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, shadowMapTextureId);
    GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_DEPTH_COMPONENT, shadowMapWidth, shadowMapHeight, 0, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, (FloatBuffer) null);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);

    // Create the framebuffer and attach the depth texture
    shadowMapFramebufferId = GL30.glGenFramebuffers();
    GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, shadowMapFramebufferId);
    GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL11.GL_TEXTURE_2D, shadowMapTextureId, 0);

    if (GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER) != GL30.GL_FRAMEBUFFER_COMPLETE)
        Voxer.error("Shadow map framebuffer creation");
    GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);

And the fragment shader sampling the depth texture :

    #version 330 core

    uniform sampler2DArray diffuseTextures;
    uniform sampler2D shadowMap;

    in vec3 pass_TextureCoord;
    in vec4 pass_ShadowCoord;
    in float pass_Lighting;

    out vec4 out_Color;

    void main(void)
        out_Color = texture(diffuseTextures, pass_TextureCoord);

        float shadowFactor = 1.0;
        vec4 shadowCoordPostW = pass_ShadowCoord / pass_ShadowCoord.w;

        if (pass_ShadowCoord.w > 0.0f && shadowCoordPostW.x > 0 && shadowCoordPostW.y > 0 && shadowCoordPostW.x < 1 && shadowCoordPostW.y < 1)
            float shadow = texture2D(shadowMap, shadowCoordPostW.xy).x;
            float epsilon = 0.00001;
            if (shadow + epsilon < shadowCoordPostW.z)
                shadowFactor = 0.5;
            // I can replace with this to show that the value is always 0
            // if(shadow == 0)
            //  shadowFactor = 0.5;

        out_Color.rgb *= shadowFactor * pass_Lighting;

I use the same projection matrix for the main camera and the sun camera (it's a perspective matrix), so I know the terrain is in the view frustum.

I used this example :

share|improve this question
up vote 5 down vote accepted

You are using sampler2D with a texture that has GL_TEXTURE_COMPARE_MODE != GL_NONE.

That is not going to work, you are invoking undefined behavior.

This line:


Tells OpenGL that when you fetch something from this texture, rather than returning the actual value, what it is supposed to do is fetch a value and then perform a comparison against the R coordinate in your texture coordinates.

What R coordinate, you ask? Precisely!

Using sampler2D with a texture that performs comparison produces undefined results because you sample a sampler2D with 2D texture coordinates. There is no 3rd coordinate to perform any comparison using.

You have two options here:

  1. You switch to sampler2DShadow and add a depth value to test to your texture coordinates
  2. You remove the line of code mentioned above, and perform shadow comparison yourself.

Personally, I would go with approach #1:

uniform sampler2DShadow shadowMap;


float epsilon = 0.00001;
float shadow  = texture (shadowMap,
                         vec3 (shadowCoordPostW.xy, shadowCoordPostW.z + epsilon));

// The result of the above `texture (...)` will be a floating-point value between
// 0 (fully blocked) and 1 (fully unblocked)
if (shadow < 1.0)
  shadowFactor = 0.5;

If you enable linear filtering of your shadow map, instead of returning exactly 1.0 or 0.0, sampler2DShadow will fetch the 4 nearest depths, perform 4 individual tests against your R coordinate and then return the average value. This gets you rudimentary shadowmap anti-aliasing; unfortunately a 2x2 window (like this uses) is not adequate in most cases, but it is a cheap form of anti-aliasing.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.