PDA

View Full Version : Omnidirectional Shadow Maps



cireneikual
04-29-2012, 06:21 PM
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!

BionicBytes
04-30-2012, 06:07 AM
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);

cireneikual
04-30-2012, 02:50 PM
I am pretty sure they are right. distance is 2000.0f. It renders fine when I use normal color textures.

BionicBytes
05-01-2012, 05:20 AM
Have you read back the depth buffers/textures to check they contain something valid?

cireneikual
05-01-2012, 01:32 PM
How would I go about doing that?

sqrt[-1]
05-02-2012, 05:23 AM
Perhaps grab an XML frame with http://code.google.com/p/glintercept/ ?

cireneikual
05-02-2012, 05:05 PM
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?

BionicBytes
05-03-2012, 02:29 AM
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);
}

cireneikual
05-03-2012, 03:41 PM
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.

BionicBytes
05-04-2012, 05:12 AM
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.

cireneikual
05-04-2012, 02:20 PM
Still nothing, it appears to be empty. Could it be possible that I need a renderbuffer in order to render to the depth textures?

BionicBytes
05-05-2012, 06:57 AM
No render buffers are just that - buffers. To display depth them you need a texture.
What is the difference betwen your znear and zfar?

cireneikual
05-05-2012, 07:14 AM
1999.99. zNear is 0.01 and zFar is 2000.0

agrum_
05-05-2012, 07:33 AM
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.

cireneikual
05-07-2012, 05:12 PM
Bumping...

cireneikual
05-10-2012, 01:20 PM
Another bump...

Dark Photon
05-11-2012, 01:13 PM
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).

cireneikual
05-11-2012, 05:18 PM
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.

Dark Photon
05-12-2012, 06:23 PM
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.

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.

cireneikual
05-13-2012, 06:33 AM
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.

cireneikual
05-16-2012, 03:16 PM
Bumping...

Dark Photon
05-16-2012, 03:36 PM
Sounds like it's time to simplify your code, and (if you don't find it through that) post a small, stand-alone test program.