2
\$\begingroup\$

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.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_COMPARE_MODE, GL30.GL_COMPARE_REF_TO_TEXTURE);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_COMPARE_FUNC, GL11.GL_LEQUAL);
    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);
    GL11.glDrawBuffer(GL11.GL_NONE);
    GL11.glReadBuffer(GL11.GL_NONE);

    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 : https://github.com/cforfang/opengl-shadowmapping

\$\endgroup\$
6
\$\begingroup\$

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:

GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_COMPARE_MODE, GL30.GL_COMPARE_REF_TO_TEXTURE);

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.

\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .