Omnidirectional Shadow Maps

I am attempting to create omnidirectional point lights that cast shadows using a depth cube map. However, when sampling the cube map, I seem to get a value of 1 in every case (making everything look black).

Here is how I generate the depth cube map and associated FBO:

glGenTextures(1, &m_cubeMapID);

    // Bind as cube map and set default settings
    glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubeMapID);


    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
    //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
    
    // Generate textures for faces
    for(int f = 0; f < 6; f++)
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, 0, GL_DEPTH_COMPONENT, m_resolution, m_resolution, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);


    // Generate mipmaps for the color texture
    //glGenerateMipmap(GL_TEXTURE_2D);


    GL_ERROR_CHECK();


    // ------------------------------ FBO ------------------------------


    glGenFramebuffers(1, &m_fboID);
    glBindFramebuffer(GL_FRAMEBUFFER, m_fboID);


    // Not using color buffer
    glDrawBuffer(GL_NONE);
    glReadBuffer(GL_NONE);


    // Attach at least on part of cube map so completeness check succeeds. Attach and detach sides later when rendering
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, m_cubeMapID, 0);
    
    GL_ERROR_CHECK();


#ifdef DEBUG
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cerr << "Could not created FBO!" << std::endl;
#endif


    // Unbind FBO, revert to main framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    // Unbind texture
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);


    GL_ERROR_CHECK();

I then render to it like this:

// For each face, render the scene    
    glBindFramebuffer(GL_FRAMEBUFFER, m_fboID);


    // Set up viewport for the map side
    glViewport(0, 0, m_resolution, m_resolution);


    // Set projection to 90 degrees
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(90.0f, 1.0f, 0.01f, distance);
    glMatrixMode(GL_MODELVIEW);


    glColorMask(false, false, false, false);


    // Side +X
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, m_cubeMapID, 0);
    glClear(GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(position.x, position.y, position.z, position.x + 1.0f, position.y, position.z, 0.0f, 1.0f, 0.0f);
    pScene->ExtractFrustum();
    pScene->Render(distance);


    // Side -X
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, m_cubeMapID, 0);


    glClear(GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(position.x, position.y, position.z, position.x - 1.0f, position.y, position.z, 0.0f, 1.0f, 0.0f);
    pScene->ExtractFrustum();
    pScene->Render(distance);


    // Side +Y
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, m_cubeMapID, 0);


    glClear(GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(position.x, position.y, position.z, position.x, position.y + 1.0f, position.z, 0.0f, 1.0f, 0.0f);
    pScene->ExtractFrustum();
    pScene->Render(distance);


    // Side -Y
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, m_cubeMapID, 0);


    glClear(GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(position.x, position.y, position.z, position.x, position.y - 1.0f, position.z, 0.0f, 1.0f, 0.0f);
    pScene->ExtractFrustum();
    pScene->Render(distance);


    // Side +Z
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, m_cubeMapID, 0);


    glClear(GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(position.x, position.y, position.z, position.x, position.y, position.z + 1.0f, 0.0f, 1.0f, 0.0f);
    pScene->ExtractFrustum();
    pScene->Render(distance);


    // Side -Z
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, m_cubeMapID, 0);


    glClear(GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(position.x, position.y, position.z, position.x, position.y, position.z - 1.0f, 0.0f, 1.0f, 0.0f);
    pScene->ExtractFrustum();
    pScene->Render(distance);


    glColorMask(true, true, true, true);


    glFlush();


    // Unbind the FBO
    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    GL_ERROR_CHECK();

Rendering to the cube map worked when using color textures, but I am not sure that it is rendering properly when using the textures as depth attachments.

Here is the shader I use for rendering the shadows:

Vertex:

uniform vec3 light_position;

// Used for shadow lookup
varying vec4 shadowDir;


void main()
{
    shadowDir = gl_Vertex * gl_ModelViewMatrix - vec4(light_position, 1.0);


    gl_Position = ftransform();


    gl_FrontColor = gl_Color;
}

Fragment:

uniform samplerCube shadowMap;

varying vec4 shadowDir;


void main()
{
    float frag_depth = length(shadowDir.xyz);


    vec3 norm_shadowDir = normalize(shadowDir).xyz;


    float shadow_map_depth = textureCube(shadowMap, norm_shadowDir).r;


    float light = 0.0;


    if(shadow_map_depth > frag_depth + 0.0005)
        light = 1.0;
        
    gl_FragColor = gl_Color;
    gl_FragColor.rgb *= light;
}

What am I doing wrong here? Thanks for any help you can offer!

Are you sure you have set near and far correctly?

// Set projection to 90 degrees
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.0f, 1.0f, 0.01f, distance);

I am pretty sure they are right. distance is 2000.0f. It renders fine when I use normal color textures.

Have you read back the depth buffers/textures to check they contain something valid?

How would I go about doing that?

Perhaps grab an XML frame with GitHub - dtrebilco/glintercept: GLIntercept is a OpenGL function call interceptor for Windows that will intercept and log all OpenGL calls ?

I figured out how to render it as a normal texture. However, it is blank. If I set glClearDepth to different values the color of the grey scale depth image I render changes, but there is no visible geometry. What am I doing wrong?

You need to linearise the data in the depth texture. The depth buffer/texture stores most values at one end of the 0.0 -> 1.0 range so they are all very close together. That’s why the image appears to be 1 shade as the difference between them is very small.
Here’s how I do mine:


/*
Render the scene depth texture to framebuffer so we can 'see' the depth'
*/
varying vec4 vertexcolor;
uniform vec2 depthrange;  //x=near, y=far
uniform sampler2D depthSampler;
float LinearizeDepth(vec2 uv)
{
  float n = depthrange.x; // camera z near
  float f = depthrange.y; // camera z far
  float z = texture2D(depthSampler, uv).x;
  return (2.0 * n) / (f + n - z * (f - n));
}
void main()
{
  float d = LinearizeDepth(gl_TexCoord[0].xy);
  float one_minus_d = 1.0 - d;
  gl_FragColor = vec4(one_minus_d, one_minus_d, one_minus_d, 1.0);
}


Thanks for your help so far, but unfortunately I still cannot see anything. I had to modify your shader to work with cube maps, but that only involved changing sampler2D to samplerCube and making the texture coordinates 3D. Again, I can change the clear depth to change the color, but it is always one continuous shade.

It may be that there is not much variation in your scene’s depth. My depth range tends to be large as I mostly render outdoor scenes.
So znear might be 1.0 and zfar 50,000.0 or something.

Still nothing, it appears to be empty. Could it be possible that I need a renderbuffer in order to render to the depth textures?

No render buffers are just that - buffers. To display depth them you need a texture.
What is the difference betwen your znear and zfar?

1999.99. zNear is 0.01 and zFar is 2000.0

Hi,
I’ve got the exact same problem except I only use sampler2D as shadow map. I should have read some thread before post mine. However, I’m quite sure the answer to your problem will also solve mine.

Bumping…

Another bump…

You’re going to need to dig into this one and provide more info that you find out through debugging.

For starters, do what one of the posters suggested and read back the depth values and print them out. To start with, don’t draw any batches – just read back the values that you’re initializing them to with glClear( GL_DEPTH_BUFFER_BIT ). Before calling this, use glClearDepth() to set the depth value that you clear the pixels in the depth buffer to. Then read them back with:

glReadPixels( 0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, buf );

And print out the values. Make sure you’re getting the clear value. Try for both 0 and 1 clear values. Then set the clear value to 1 and go render with your shader. Use this to diagnose the readback. Modify your shader incrementally to determine where the bad values are coming from (or establish that you are in-fact getting reasonable values).

I already was able to render the clear depth to the FBO, both 0, 1, and stuff in between, as a quad to the screen that showed up with varying shades of grey.

However, I tried glReadPixels anyways, and get 0 every time. Since the quad rendering works though, I assume I just did not set up glReadPixels properly. I am not even sure how glReadPixels is supposed to work with a cube map anyways.

The shader may be a problem in the future, but right now, no geometry is showing up on the depth buffer at all. I get geometry when using the color textures instead of depth textures though.

I haven’t done it, but doesn’t seems like there’s anything special. glReadPixels reads from the active read buffer of the active READ framebuffer. You’ve bound a specific face of the cubemap to the framebuffer. So given that you’ve set the read buffer and read framebuffer properly, it’ll read from the texture through that framebuffer.

I accidentally ran it after I unbound the FBO :p, but now I have it reading the pixels properly. It is just the clear value everywhere, just like when I rendered it as a quad to the screen. So it must be how the scene is being rendered to the depth textures. It can’t be the scene Render() function, since it works perfectly with color textures.