Shadow Mapping problem

Started by
4 comments, last by haegarr 16 years, 12 months ago
Gentlemen, I'm trying to implement shadow mapping based on Paul's tutorial. My code follows his very closely, but I'm having a few problems. (1)I'm getting some weird flicker. Paul's doesn't give this, so I'm fair-certain it's not a graphics card setting like V-Sync. I'm culling front faces when creating the depth texture to avoid the z-fighting. So I'm not sure what's going on there. Anyone have any insights on this? (2)The shadows don't seem quite correct. I've pointed out the error here. I don't know if this is a normal thing with shadow mapping or something wrong with my specific implementation. Here's the code if you want to peek around (it's a bit long):

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	/** Pass 1
	 * In this pass, render the scene from the light's perspective.  Then copy
	 * the depth buffer to the shadow texture.
	 */
	
	//Setup the projection matrix and store the results for use when calculating the texture matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, 1.0, 2.0, 8.0);
	glGetFloatv(GL_PROJECTION_MATRIX, lightProjectionMatrix);

	//Setup the modelview matrix to look from the light's perspective, and store the results
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(lightPosition[0], lightPosition[1], lightPosition[2], 0, 0, 0, 0, 1, 0);
	glGetFloatv(GL_MODELVIEW_MATRIX, lightViewMatrix);

	//Change the viewport so that everything rendered fits inside the shadow map
	glViewport(0, 0, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE);

	//Cull front faces to avoid z-fighting
	glCullFace(GL_FRONT);

	//Set the simplest shading model and disable writes to the color buffer for
	//a speed boost
	glDisable(GL_LIGHTING);
	glShadeModel(GL_FLAT);
	glColorMask(0,0,0,0);

	//Render the scene, which should only make changes to the depth buffer
	drawScene();

	//Copy the rendered scene into the shadow texture
	glBindTexture(GL_TEXTURE_2D, shadowTexture);
	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE);

	//Turn on normal render settings so that we can draw normally
	glCullFace(GL_BACK);
	glShadeModel(GL_SMOOTH);
	glColorMask(1,1,1,1);
	
	/** Pass 2
	 * Render the scene as though it were completely shadowed.
	 */

	//The depth buffer should be the only thing that was written to in the previous pass,
	//so go ahead and clear it.
	glClear(GL_DEPTH_BUFFER_BIT);

	//Restore the normal viewport
	glViewport(0,0,resX, resY);

	//Setup the normal projection matrix.
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, (float)resX/(float)resY, 1.0, 100.0);
	
	//Look in the camera's direction
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(cameraPosition[0], cameraPosition[1], cameraPosition[2], 0, 0, 0, 0, 1, 0);

	//Enable dim light
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT1);
	glLightfv(GL_LIGHT1, GL_POSITION, lightPosition);
	glLightfv(GL_LIGHT1, GL_AMBIENT, matDimWhite);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, matDimWhite);
	glLightfv(GL_LIGHT1, GL_SPECULAR, matBlack);

	//Draw the scene as if it were shadowed
	drawScene();

	/** Pass 3
	 * Render the parts of the scene that aren't shadowed.
	 */

	//Enable a normal white light
	glLightfv(GL_LIGHT1, GL_DIFFUSE, matWhite);
	glLightfv(GL_LIGHT1, GL_SPECULAR, matWhite);

	//Compute the texture matrix, T = bias * lightProjection * lightView
	//Here we make use of OpenGL's texture multiplication methods to do the multiplication.
	glPushMatrix();
		glLoadIdentity();
		glLoadMatrixf(bias);
		glMultMatrixf(lightProjectionMatrix);
		glMultMatrixf(lightViewMatrix);
		glGetFloatv(GL_MODELVIEW_MATRIX, textureMatrix);
	glPopMatrix();
	
	float row0[] = {textureMatrix[0], textureMatrix[4], textureMatrix[8], textureMatrix[12]};
	float row1[] = {textureMatrix[1], textureMatrix[5], textureMatrix[9], textureMatrix[13]};
	float row2[] = {textureMatrix[2], textureMatrix[6], textureMatrix[10], textureMatrix[14]};
	float row3[] = {textureMatrix[3], textureMatrix[7], textureMatrix[11], textureMatrix[15]};
	
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
	glTexGenfv(GL_S, GL_EYE_PLANE, row0);
    glEnable(GL_TEXTURE_GEN_S);

    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    glTexGenfv(GL_T, GL_EYE_PLANE, row1);
    glEnable(GL_TEXTURE_GEN_T);

    glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    glTexGenfv(GL_R, GL_EYE_PLANE, row2);
    glEnable(GL_TEXTURE_GEN_R);

    glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
    glTexGenfv(GL_Q, GL_EYE_PLANE, row3);
    glEnable(GL_TEXTURE_GEN_Q);

	glBindTexture(GL_TEXTURE_2D, shadowTexture);
	glEnable(GL_TEXTURE_2D);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
	glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY);
	glAlphaFunc(GL_GEQUAL, 0.99f);
    glEnable(GL_ALPHA_TEST);

	drawScene();

	glDisable(GL_TEXTURE_2D);

    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_GEN_R);
    glDisable(GL_TEXTURE_GEN_Q);
    glDisable(GL_LIGHTING);
    glDisable(GL_ALPHA_TEST);

	glFinish();
	glutSwapBuffers();
}


Thanks a lot, --Brian
Advertisement
Hi,

my OpenGL's a bit rusty, so I can't really comment on your code, but here are some pointer that may help you on your way.

About the Z-fighting: my first hunch would be to add a small bias to the depths in the shadowmap, but I see you added a bias using a matrix.

Have you tried rendering the contents of the actual shadowmap to a file, so you can make sure that a) the light is at the right position, b) it's facing the right direction, c) the objects are where they belong in world space.

----------------------------- Jan-Jaap Severs Grendel Games[ Grendel Games ]
Hello,

You mean flickering as in flickering of the lit areas? This is probably because you need to offset your geometry in some way. As I understood Paul's tutorial, the biasmatrix does not do this for you. I believe the biasmatrix is used to change the coordinate output to screencoordinates for use with the texture (depthmap).

So, to get to the point of offsetting your geometry. This is also called a bias. The point is that, when you are storing the scene's depth in your depthmap, the newly drawn depths of the lit areas are approximately the same as the saved values. Some are a little higher, some a little lower. Outside the shadows, the values in the depthmap should always be greater than or equal to the depth in your final scene. To make sure this is always the case, you simply make it a little greater than it really is. So, in short, you move the geometry a little away from the light.

One way to do this, is by using glEnable(GL_POLYGON_OFFSET_FILL) with glPolygonOffset(scale,units). This way you can scale and offset your depth. Try it out to see what works for your scene.

Good luck,
Ignifex
The glPolygonOffset solution goes a long way toward fixing the flickering problem. It's not perfect, but that may just be the result of the values I'm choosing. Unfortunately, those values seem to need to be different depending on the position of the light... so it looks like I've still got a bit of playing around left.

If you have any insights about the wonky shadow glitch, feel free to toss them along.

Thanks,
--Brian
Well, I have seen the problem before in one of my own scenes. I decided to blame it on the area being outside the shadowmap. I moved the light further away, which seemed to solve it. Try drawing your shadowmap instead of your scene to see if the area is included. You'll need to use an orthogonal view with something like glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_INTENSITY) to make your shadowmap's depth appear as intensity.

The offset value is one of the things that makes shadow mapping imperfect. It's mainly visible around the edges of a wall for example.
Yes, polygon bias is definitely view (or light position, in this case) dependent, and its a pain to outweigh benefits and artifacts. However, using front face culling (i.e. "2nd depth shadow mapping") is intended to avoid polygon bias for this purpose at all. So there might be another problem not yet identified?!

This topic is closed to new replies.

Advertisement