3

I can't get shadow mapping to work in my application. I try to render a quad bike and view its shadow on a floor beneath it. Here's some of my code. Texture creation:

    // Create a depth texture
    glGenTextures(1, &depth_texture);
    glBindTexture(GL_TEXTURE_2D, depth_texture);
    // Allocate storage for the texture data
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32 ,1600, 900, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    // Set the default filtering modes
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Set up wrapping modes
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);
    // Create FBO to render depth into
    glGenFramebuffers(1, &depth_fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);
    // Attach the depth texture to it
    glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
    depth_texture, 0);
    // Disable color rendering as there are no color attachments
    glDrawBuffer(GL_NONE);
    //check fbo status
    GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(result != GL_FRAMEBUFFER_COMPLETE)
        throw std::runtime_error("shadow mapping framebuffer error");
    //bind default framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

render to depth texture:

progShadow.Use();
glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);

glClear(GL_DEPTH_BUFFER_BIT);

glm::mat4 shadowProjection = glm::frustum(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 100.0f);
glm::mat4 shadowView = glm::lookAt(light.position, glm::vec3(0,0,0), glm::vec3(0,1,0));
glm::mat4 shadowModel(1);
if(g_rotate)
    shadowModel = glm::rotate((float)clock() / (float)CLOCKS_PER_SEC, glm::vec3(0,1,0));
glm::mat4 shadowMVP = shadowProjection * shadowView * shadowModel;
progShadow.SetUniform("MVPMatrix", shadowMVP);
quadBike.Draw();

I also use a "test" shader program that renders my depth texture. Here's what it looks like. image

So I guess I'm good until now. Now I render the scene normally.

glBindTexture(GL_TEXTURE_2D, depth_texture);
prog.Use();//main program
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 shadowBias = glm::mat4(0.5, 0.0, 0.0, 0.0,
                                 0.0, 0.5, 0.0, 0.0,
                                 0.0, 0.0, 0.5, 0.0,
                                 0.5, 0.5, 0.5, 1.0);
glm::mat4 ShadowBiasMVP = shadowBias * shadowMVP;
prog.SetUniform("ShadowBiasMVP", ShadowBiasMVP);

//draw quadBike and floor
...

Relevant parts of my vertex shader:

#version 430

...

out vec4 shadowCoord;

void main()
{
    gl_Position = ProjectionMatrix * CameraMatrix * ModelMatrix * vec4(vertex, 1.0);
    shadowCoord = ShadowBiasMVP * vec4(vertex, 1.0);
    ...
}

Relevant parts of my fragment shader:

#version 430

...

uniform sampler2D shadowMap;
in vec4 shadowCoord;

void main()
{
    ...

    float visibility = 1.0;
    if ( texture(shadowMap, shadowCoord.xy).z <  shadowCoord.z)
        visibility = 0.0;

    ...
}

Now the problem is that I get a scene that is fully dark as if it was all covered by shadow. Only when the light is really close to the quad bike, it renders it normally. (the depth texture is visible on the right side because it's rendered with a different program. I use it for testing) scene

What am I doing wrong?

McLovin
  • 3,295
  • 7
  • 32
  • 67
  • The purpose of the bias matrix is to rescale (x **0.5**) and offset (+ **0.5**) your `xy` (texture coordinates) and `z` (depth) into normalized texture coordinate and depth range [**0.0**,**1.0**] from clip-space ([**-w**,**w**]). To finish the transformation that the bias matrix is supposed to do, you need to divide by `w` (right now your math only works if `shadowCoord.w` is **1.0**, which will not be the case if you are using perspective projection). – Andon M. Coleman Dec 27 '14 at 15:06

1 Answers1

2
  1. You should read a grayscale depthtexture at the first component.

    texture(shadowMap, shadowCoord.xy).r or

    texture(shadowMap, shadowCoord.xy).x

  2. The Shadow Coordinates should be dehomogenized (divided by w) after interpolation.

    -> in fragment shader: shadowPos = shadowPos/shadowPos.w;

  3. If no other techniques like polygon offset is used you need to substract a bias from the shadow depth value to prevent self shadowing.

Here is an example function to calculate shadows in fragment shader. Note: It is part of a deferred renderer, that is why matrix multiplication is done in the fragment shader.

float calculateShadow(vec3 position){
    vec4 shadowPos = depthBiasMV * vec4(position,1);
    shadowPos = shadowPos/shadowPos.w;


     float bias =  0.0012;
     float visibility = 1.0f;
     if ((shadowPos.x < 0 || shadowPos.x > 1 || shadowPos.y < 0 || shadowPos.y > 1 || shadowPos.z < 0 || shadowPos.z > 1)){
       visibility = 1.0f;
     }else{
        float shadowDepth = texture(depthTex, shadowPos.xy).r;
        if(shadowDepth<shadowPos.z-bias)
            visibility = 0.0f;
     }
     return visibility;

}
Community
  • 1
  • 1
dari
  • 2,255
  • 14
  • 21
  • Using the function you posted, I get a really large shadow on the floor. [link](http://s30.postimg.org/pbzx2fy41/scenee.png) – McLovin Dec 25 '14 at 20:16
  • Still stuck on this :(. Could anyone maybe post a link to a good guide that widely explains this topic? Using [this](http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/) tutorial didn't solve my problem. – McLovin Dec 26 '14 at 20:07
  • The thing is basic shadow mapping is actually pretty simple, so you probably have somewhere a stupid mistake. But maybe reading an other tutorial will help. http://ogldev.atspace.co.uk/www/tutorial23/tutorial23.html – dari Dec 27 '14 at 17:52
  • I got it to work. The problem was in my vertex shader. "shadowCoord" should have been declared as `shadowCoord = ShadowBiasMVP * ModelMatrix * vec4(vertex, 1.0);`. Now I'm debugging the program and testing different values. Could you answer the next question? Correct me if I'm wrong: in the fragment shader, shadowPos.z is the distance from the light source to the surface, so why do I get different (and wrong) results if I replace it with: `vec3 surfacePos = vec3(ModelMatrix * vec4(fragVert, 1)); float surfaceToLight = length(surfacePos - light.position);//the new value` What's the difference? – McLovin Dec 30 '14 at 21:01
  • Yes it is correct that `shadowPos.z` is the distance from the lightsource to the fragment, but this distance ist not in world space. Since the shadow map (the texture rendered by the light source) is in 'shadow-space' you need to transform your vertex to shadow-space, too. Only when both positions (shadow-map and shadowPos) are in shadow-space, the distance comparision makes sense. – dari Dec 30 '14 at 21:14
  • One more quick question. From debugging I noticed that both shadowPos.z and shadowDepth are positive. Shouldn't they be negative, because the depth of each fragment goes along the -z axis? – McLovin Dec 30 '14 at 21:22
  • After perspective transformation and dividing by w the x,y and z values are in the range [-1,1]. The bias matrix then scales the range to [0,1] by first multiplying with 0.5 and then adding 0.5. – dari Dec 30 '14 at 21:43