3
votes

I'm doing shadow mapping in OpenGL - as such I've created a frame buffer object where I render the depth of the scene from the view of a light.

glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_buffer);

glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buffer);  

glGenTextures(1, &color_texture);
glBindTexture(GL_TEXTURE_2D, color_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture, 0);

glGenTextures(1, &depth_texture);
glBindTexture(GL_TEXTURE_2D, depth_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);

This then renders the scene from the light perspective as normal, all as you would expect. The only addition is I'm using a custom shader to render the depth of the scene also to the "color_texture".

varying vec4 screen_position;

void main()
{
  screen_position = gl_ModelViewProjectionMatrix * gl_Vertex;
  gl_Position = screen_position;
}

--------------

varying vec4 screen_position;

void main()
{
  float depth = screen_position.z / screen_position.w;
  gl_FragColor = vec4(depth, depth, depth, 1.0);
}

I can write these two textures "color_texture" and "depth_texture" to the screen using a full screen quad. Both are indeed depth maps and look correct. Now for the weird bit.

When it comes to actually rendering the shadows on objects sampling the "color_texture" depth works fine, but when I switch to sampling "depth_texture", the depth is different by some scale and some constant.

When I added some fudge factor numbers to this sampled depth I could sort of get it to work, but it was really difficult and it just felt horrible.

I really can't tell what is wrong, technically the two textures should be identical when sampled. I can't carry on using "color_texture" due to the accuracy of RGB. I really need to switch but I can't for the life of me work out why the depth texture gives a different value.

I've programmed shadow mapping several times before and it isn't a concept that is new to me.

Can anyone shed any light on this?

1

1 Answers

6
votes

There are some issues with your shader. First, screen_position is not a screen position; that's the clip-space vertex position. Screen space would be relative to your monitor, and therefore would change if you moved the window around. You never get screen-space positions of anything; OpenGL only goes down to window space (relative to the window's location).

Second, this:

float depth = screen_position.z / screen_position.w;

does not compute depth. It computes the normalized-device coordinate (NDC) space Z position, which ranges from [-1, 1]. Depth is a window-space value, which comes after the NDC space value is transformed with the viewport transform (specified by glViewport and glDepthRange). This transform puts the depth on the [0, 1] range.

Most importantly third, you're doing this all manually; let GLSL do it for you:

void main()
{
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

--------------

void main()
{
  gl_FragColor = vec4(gl_FragCoord.zzz, 1.0);
}

See? Much better.

Now:

When it comes to actually rendering the shadows on objects sampling the "color_texture" depth works fine, but when I switch to sampling "depth_texture", the depth is different by some scale and some constant.

Of course it is. That's because you believed that the depth was the NDC Z-value. OpenGL writes the actual window-space Z value as the depth. So you need to work with window space. OpenGL is nice enough to provide some uniforms to your fragment shader to make this easier. You can use those uniforms to transform NDC-space Z values to window space Z values.