shadow maps covering lit geometry

I’ve got a problem with my shadow mapping, I thought I’d be able to solve it, but since all this time, I haven’t found anything wrong with them.
I was helped by paul’s project for making them (if I can remember correctly).

Here is the topic:

There’s a plane surface on which a cube is lying on it, and a light rotating around the cube. I can see well the shadow silhouette on the surface and on the cube, but the shadow doesn’t end correctly: it still continues in the direction and sense light-cube-shadow and seems not to end. I’m very embarassed with that. I can’t give any screenshots for the moment, but here is the full source code of it (note that you’ll need to load extensions if you’re not under Linux with GL 2):

#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif

#include <iostream>
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <cmath>

using namespace std;

GLfloat l_pos[] = {2,3,-2,1};
GLfloat l_dir[] = {0,-1,-1};
GLfloat l_dif[] = {.5,.5,.5,1.};

GLfloat white[] = {1.,1.,1.,1.};
GLfloat black[] = {0.,0.,0.,1.};

GLfloat light_projection_matrix[16];
GLfloat light_view_matrix[16];

GLfloat sm[] = {1,0,0,0};
GLfloat tmat[]={0,1,0,0};
GLfloat rm[] = {0,0,1,0};
GLfloat qm[] = {0,0,0,1};

GLfloat bias_matrix[] = 
{
   .5, .0, .0, .0,
   .0, .5, .0, .0,
   .0, .0, .5, .0,
   .5, .5, .5, 1.0
};

const GLuint tex_size = 512;
GLuint shadow_map_tex;
GLubyte shadow_map_data[tex_size*tex_size];

void init()
{  
   glClearColor (0.,0.,0.,0.);
   glShadeModel (GL_SMOOTH);
   glEnable (GL_DEPTH_TEST);
   glDepthFunc (GL_LESS);
   glEnable (GL_CULL_FACE);
   glCullFace (GL_BACK);

   glGenTextures (1, &shadow_map_tex);
   glBindTexture (GL_TEXTURE_2D, shadow_map_tex);
   glTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, tex_size, tex_size, 
	 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,0);
   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);

   glPolygonOffset (1.1,4.);
}

void reshape (int w, int h)
{
   glViewport (0,0,w,h);
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity();
   gluPerspective (100, 4./3., .0125, 1200);
   glMatrixMode (GL_MODELVIEW);
   glLoadIdentity();
}

void
RenderScene()
{
   GLfloat tp_mat[] = {.9,.2,.1,1.};
   GLfloat te_mat[] = {.0,.8,.2,1.};
   GLfloat li_mat[] = {.7,.7,.0,1.};
   
   glPushMatrix();
   glTranslatef (0,1,0);
   glColor3f (1,0,0);
   glMaterialfv (GL_FRONT, GL_AMBIENT, tp_mat);
   glMaterialfv (GL_FRONT, GL_DIFFUSE, tp_mat);
   glutSolidCube (1);
   glPopMatrix();

   glColor3f (0,1,1);
   glMaterialfv (GL_FRONT, GL_AMBIENT, te_mat);
   glMaterialfv (GL_FRONT, GL_DIFFUSE, te_mat);
   glBegin (GL_QUADS);
   glNormal3f (0,1,0);
   glVertex3f (-5,0,-5);
   glNormal3f (0,1,0);
   glVertex3f (-5,0,5);
   glNormal3f (0,1,0);
   glVertex3f (5,0,5);
   glNormal3f (0,1,0);
   glVertex3f (5,0,-5);
   glEnd();
}

void 
display()
{
   static GLfloat alpha = .0;
   alpha += .001;
   
   l_pos[0] = 2 * std::cos (alpha);
   l_pos[2] =-2 * std::sin (alpha);
   
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
   glMatrixMode (GL_MODELVIEW);
   glLoadIdentity();

   glPushMatrix();
   glLoadIdentity();
   gluPerspective (45., 1., 0.5, 800.);
   
   glGetFloatv (GL_MODELVIEW_MATRIX, light_projection_matrix);
   glLoadIdentity();
   gluLookAt (l_pos[0],l_pos[1],l_pos[2], 0,0,0, 0,1,0);
   glGetFloatv (GL_MODELVIEW_MATRIX, light_view_matrix);
   glPopMatrix();
   
   //
   // First pass:
   // From light's point of view
   // 
   
   glCullFace (GL_FRONT);
   glColorMask (GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);

   glMatrixMode (GL_PROJECTION);
   glLoadMatrixf (light_projection_matrix);
   glMatrixMode (GL_MODELVIEW);
   glLoadMatrixf (light_view_matrix);
   glViewport (0,0,tex_size,tex_size);
   
   RenderScene();
   
   glBindTexture (GL_TEXTURE_2D, shadow_map_tex);
   glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 0, 0, tex_size, tex_size);

   glColorMask (GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
   glCullFace (GL_BACK);

   // 
   // Second pass:
   // normal render
   // 

   glClear (GL_DEPTH_BUFFER_BIT);
   
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity();
   gluPerspective (90, 4./3., 1.0, 100.);
   glMatrixMode (GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt (4,4,0,0,0,0,0,1,0);
   glViewport (0,0,800,600);
   
   glLightfv (GL_LIGHT0, GL_POSITION, l_pos);
   glLightfv (GL_LIGHT0, GL_AMBIENT, black);
   glLightfv (GL_LIGHT0, GL_DIFFUSE, black);
   glLightfv (GL_LIGHT0, GL_SPECULAR, black);
   glEnable (GL_LIGHT0);
   glEnable (GL_LIGHTING);
   
   RenderScene();

   // 
   // Third pass:
   // do the shadows
   // 
   
   glDepthFunc (GL_EQUAL);
   
   glLightfv (GL_LIGHT0, GL_DIFFUSE, l_dif);
   glLightfv (GL_LIGHT0, GL_SPECULAR, white);
   
   glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
   glTexGenfv (GL_S, GL_EYE_PLANE, sm);
   glEnable (GL_TEXTURE_GEN_S);
   
   glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
   glTexGenfv (GL_T, GL_EYE_PLANE, tmat);
   glEnable (GL_TEXTURE_GEN_T);
   
   glTexGeni (GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
   glTexGenfv (GL_R, GL_EYE_PLANE, rm);
   glEnable (GL_TEXTURE_GEN_R);
   
   glTexGeni (GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
   glTexGenfv (GL_Q, GL_EYE_PLANE, qm);
   glEnable (GL_TEXTURE_GEN_Q);

   glBindTexture (GL_TEXTURE_2D, shadow_map_tex);
   glEnable (GL_TEXTURE_2D);

   glMatrixMode (GL_TEXTURE);
   glLoadIdentity();
   glMultMatrixf (bias_matrix);
   glMultMatrixf (light_projection_matrix);
   glMultMatrixf (light_view_matrix);
   glMatrixMode (GL_MODELVIEW);

   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);

   RenderScene();

   glDisable (GL_ALPHA_TEST);
   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_LIGHT0);

   glDepthFunc (GL_LESS);

   glPushMatrix();
   glColor3f (1,1,0);
   glTranslatef (l_pos[0],l_pos[1],l_pos[2]);
   glEnable (GL_NORMALIZE);
   glScalef (.1,.1,.1);
   glutSolidSphere (1,16,16);
   glDisable (GL_NORMALIZE);
   glPopMatrix();

   glFinish();
   glGetError();
   glutSwapBuffers();
   glutPostRedisplay();
}

void KeyFunc (unsigned char key, int x, int y)
{
   switch (key){
      case 27:
	 exit (0);
   }
}

void idle (void)
{
   glutPostRedisplay();
}

int main (int argc, char**argv)
{
   glutInit (&argc, argv);
   glutInitWindowPosition (0,0);
   glutInitWindowSize (800,600);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
   glutCreateWindow ("Shadow mapping (testing)");
	
   init();
	
   glutDisplayFunc (display);
   glutReshapeFunc (reshape);
   glutKeyboardFunc (KeyFunc);
   glutMainLoop();

   return 0;
}

Thanks for any help about that problem.

try using GL_CLAMP_TO_EDGE wrapping of depth texture.

I replaced:

glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

by:

glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

But this changed nothing.

Could this be related and/or help (if nothing else it points out good-to-know-about limitations of certain h/w)?

I looked at it but didn’t find out anything that could help me. I’ll try to give some pictures, but I really can’t promise it.

Its a common problem when doing shadow maps, because you are trying to do depth map comparison with pixels that are outside the shadow map boundary (i don’t know whether your problem lies in this domain. But i have encountered the exact same artifacts that you mentioned and my problem was the same, as i mentioned above). If you set wrapping to repeat then the shadow of your object (teapot, or sphere or whatever) will start repeating. Another problem with shadow maps is that they back project if care is not taken. I have not used FFP for shadow mapping extensively and don’t know how to stop this, but you can easily get rid of both these problems by simple spot light attenuation equation in a fragment shader. That way, pixels outside the “lit” area of spot light are shaded dark and artifacts are not visible anymore (and that includes polygons at the back of spot light to take care of back projections).

Is it really a common problem ? Shadow maps have been (and are still) used for years, in many games, modelers and so on. I never have seen that in any examples I tried neither in all the apps I’ve used.

Maybe light attenuation should help, but I really wonder how that can completly avoid that: this is not simply artifacts like aliasing, this is a shadow that doesn’t stop as it ought to.

Just try my sources, I guess getting extension addresses is not required as the only extension I use has no procedure but only constants (ARB_shadow if I remember well).

About shaders, why not. But I don’t want to use shaders to overcome issues I wasn’t able to solve without them. And many programs using shadow maps do not use shaders to avoid those ‘mis-drawings’.

Anyway, thank you for your help.

That does not mean that people leave it there in commercial applications :slight_smile: .
Back projections is an inherent “problem” with depth maps, and are infact mentioned in decent papers on shadow mapping. As far as the problem at hand is concerned, the reason (this is my assessment only) for the shadow extending is that you are trying to sample regions outside the shadow map (think of it like sampling outside a texture boundary when clamping is given. You will get the texel at the boundary, averaged with neighboring pixels, including border, and without border if clamp to edge is specified). So you are getting the edge shadow map texel, which by chance contains a depth value lesser than your plane which is supposed to be lit (i.e. not in shadow).

One of the better ways of solving shadow mapping problems is by viewing the scene from the light POV, so that you can see what’s actually being rendered into the shadow map.

Originally posted by jide

About shaders, why not. But I don’t want to use shaders to overcome issues I wasn’t able to solve without them. And many programs using shadow maps do not use shaders to avoid those ‘mis-drawings’.

The reason why i suggested shaders was that they are a lot easier to “debug” since the entire functionality is hand written. And you are free to play around with parameters. You can also put different colors for different conditions (something like printf style debugging), and then view the final frame buffer and try to figure out the problem based on color output.

Hope you understand.

I use a shader when doing shadow mapping and doing so makes it trivial to correct the back projection issue by setting the frag color to black if the w component of the projected tex coords is negative.

If you can, by all means use shaders as they make life much easier in these cases. You’re not going to be branded as inferior if you do. That’s what development is all about, doing whatever you need to to solve the problem the easiest way possible. That’s the smartest move a dev can make. Sure there may be apps that solve many of the issues of shadow maps without shaders, but that’s only b/c they couldn’t use shaders for their target (or shaders didn’t exist yet). Forget about fixed function, shaders is where everything is going.

-SirKnight

Ok WTF happened to my account? Everything was reset!

test

EDIT: LOL, what in the hell?

Sorry for hijacking your thread like this.

I don’t care about beeing seen as an inferior or so: everyone can judge me as long as he accepts I’ll judge him in a future, and as everyone has his own bottlenecks… :slight_smile:

I would have liked to find a solution without needing any shaders at all. But I can see most of you (highly) suggest me to use them. I know shaders, shaders, shaders… But I just succeed to emulate most important features from the fixed pipeline (transform, lighting – with some differences, texturing).

I don’t aim graphic card not supporting shaders, but I would really like to avoid them in that case. It just seems to me that it would be better (from my own programmer’s view).

PS: about the problem you encountered: This appends sometimes, I guess when there were some problems with the network or maybe the server, I already have had that.
Were you scared to become a newbie again ? :stuck_out_tongue:

But I don’t know if I’ll appologize you a day :smiley:

regards

Originally posted by jide:

Were you scared to become a newbie again ? :stuck_out_tongue:

Yeah I was afraid that I was going to have to open up NeHe tutorial #1 all over again. :smiley:

I do understand you wanting to clear up the problems with a simple function call or flag, hell I’d prefer that too. :slight_smile: But with how things are, using a shader really is the easiest solution.

I have a shadow map demo that uses Cg and it demonstrates getting rid of the back projection and some other stuff. It has soft-shadows too and shows how to use my FBO class for shadow mapping. I’ll upload it later and post the link if you’re interested. It uses glut and I think it will compile in linux as is.

-SirKnight