shadow mapping problem (color-related)

Started by
5 comments, last by PiNtoS 15 years, 8 months ago
I have a bizarre problem with my shadow mapping code. The shadows work fine, but a little test sphere seems to lose its color. Screenshots: With shadows off (note how the sphere is white). And with shadows on. It's probably something really stupid, maybe even having nothing to do with the shader, but I'm stumped. Here's the vertex shader (GLSL):

varying vec4 diffuse;
varying vec3 normal, lightDir;

varying vec4 shadowTexCoord;

void main()
{
	normal = normalize(gl_NormalMatrix * gl_Normal);
	lightDir = vec3(gl_LightSource[0].position - gl_ModelViewMatrix * gl_Vertex);
	diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
	
	shadowTexCoord = gl_TextureMatrix[0] * gl_Vertex;

	gl_FrontColor = gl_Color;
	gl_Position = ftransform();
}


and the fragment shader:

varying vec4 diffuse;
varying vec3 normal, lightDir;

varying vec4 shadowTexCoord;
uniform sampler2DShadow shadowMap;

float shading()
{
	vec3 n = normalize(normal);
	vec3 dir = normalize(lightDir);
	return max(dot(n, dir), 0.0);
}

void main()
{
	float shadow = shadow2DProj(shadowMap, shadowTexCoord).x;
	vec4 color = gl_Color * diffuse;
	gl_FragColor = vec4(shadow * shading() * color.rgb, color.a);
}


Transforming the texture matrix:

glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(.5, .5, .5);
glScalef(.5, .5, .5);
glMultMatrixd(lightProjectionMatrix);
glMultMatrixd(lightModelViewMatrix);
glMatrixMode(GL_MODELVIEW);


and before we render the scene with shadows:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);


and finally, I guess, if it's relevant, drawing the damn (white!) ball:

GLUquadric *pQuad = gluNewQuadric();
gluQuadricNormals(pQuad, GLU_SMOOTH);

glColor3f(1, 1, 1);
glTranslatef(bpos[0], bpos[1], bpos[2]);
gluSphere(pQuad, .5, 20, 20);
glTranslatef(-bpos[0], -bpos[1], -bpos[2]);

gluDeleteQuadric(pQuad);



Any help would be great!
Advertisement
Have you disabled texture when drawing the sphere?
Or if the sphere is actually textured (too?)...then generate texture coordinates in your quadric with gluQuadricTexture( pQuad, GL_TRUE );

I'll leave shader comments to other posters, as I'm only recently migrating to shaders after many many years. Btw: yours inspires me more so!
Actually, nothing's textured.

As a followup:

The problem is definitely somewhere in the shadow business: for some reason, it views the ball as being in shadow (but I don't know why.)
Bump, with new info:

Here's a screenshot showing:

Upper left: final render
Lower left: the shadow map
Lower right: The distance from each point to the light
Upper right: The corresponding point on the shadow map (i.e., the depth of the nearest occluder)

Look at the lower right - the ball is farther away from the light than the box, but it's darker (i.e., it's computed as closer). This seems to the main problem (but I'm not sure). Is there any reason it would do this? Is there an orientation bit that I may be flipping, or something?

Although I'm not sure it's relevant, here's the entire scene drawing code:

// draw floorfloat size = 10;glColor3f(0, 0, 1);glBegin(GL_QUADS);	glNormal3f(0, 0, 1);	glVertex3f(-size, -size, 0);	glVertex3f(-size, size, 0);	glVertex3f(size, size, 0);	glVertex3f(size, -size, 0);glEnd();// draw ballGLUquadric *pQuad = gluNewQuadric();gluQuadricNormals(pQuad, GLU_SMOOTH);glColor3f(1, 1, 1);float bpos[3] = { bdist * cos(bangle), bdist * sin(bangle), bheight };glTranslatef(bpos[0], bpos[1], bpos[2]);gluSphere(pQuad, .5, 20, 20);glTranslatef(-bpos[0], -bpos[1], -bpos[2]);gluDeleteQuadric(pQuad);// draw air boxfloat box = 2;glColor3f(0, 1, 0);glTranslatef(-bpos[0], -bpos[1], bpos[2]);glBegin(GL_QUADS);	glNormal3f(0, 0, 1);	glVertex3f(-box / 2, -box / 2, box);	glVertex3f(box / 2, -box / 2, box);	glVertex3f(box / 2, box / 2, box);	glVertex3f(-box / 2, box / 2, box);	glNormal3f(1, 0, 0);	glVertex3f(box / 2, -box / 2, 0);	glVertex3f(box / 2, box / 2, 0);	glVertex3f(box / 2, box / 2, box);	glVertex3f(box / 2, -box / 2, box);	glNormal3f(0, 1, 0);	glVertex3f(box / 2, box / 2, 0);	glVertex3f(-box / 2, box / 2, 0);	glVertex3f(-box / 2, box / 2, box);	glVertex3f(box / 2, box / 2, box);	glNormal3f(-1, 0, 0);	glVertex3f(-box / 2, box / 2, 0);	glVertex3f(-box / 2, -box / 2, 0);	glVertex3f(-box / 2, -box / 2, box);	glVertex3f(-box / 2, box / 2, box);	glNormal3f(0, -1, 0);	glVertex3f(-box / 2, -box / 2, 0);	glVertex3f(box / 2, -box / 2, 0);	glVertex3f(box / 2, -box / 2, box);	glVertex3f(-box / 2, -box / 2, box);glEnd();glTranslatef(bpos[0], bpos[1], -bpos[2]);// draw boxglColor3f(1, 0, 0);glBegin(GL_QUADS);	glNormal3f(0, 0, 1);	glVertex3f(-box / 2, -box / 2, box);	glVertex3f(box / 2, -box / 2, box);	glVertex3f(box / 2, box / 2, box);	glVertex3f(-box / 2, box / 2, box);	glNormal3f(1, 0, 0);	glVertex3f(box / 2, -box / 2, 0);	glVertex3f(box / 2, box / 2, 0);	glVertex3f(box / 2, box / 2, box);	glVertex3f(box / 2, -box / 2, box);	glNormal3f(0, 1, 0);	glVertex3f(box / 2, box / 2, 0);	glVertex3f(-box / 2, box / 2, 0);	glVertex3f(-box / 2, box / 2, box);	glVertex3f(box / 2, box / 2, box);	glNormal3f(-1, 0, 0);	glVertex3f(-box / 2, box / 2, 0);	glVertex3f(-box / 2, -box / 2, 0);	glVertex3f(-box / 2, -box / 2, box);	glVertex3f(-box / 2, box / 2, box);	glNormal3f(0, -1, 0);	glVertex3f(-box / 2, -box / 2, 0);	glVertex3f(box / 2, -box / 2, 0);	glVertex3f(box / 2, -box / 2, box);	glVertex3f(-box / 2, -box / 2, box);glEnd();
Have you disabled your shadowing shader after your first draw?

Have you still got the active texture to be a FBO or PBuffer?

If your not needing texturing at the moment but a glDisable(GL_TEXTURE_2D) before your drawing code for your second pass and see it improves anything.

Jamie
Quote:Have you disabled your shadowing shader after your first draw?


By "first draw" do you mean the first pass? If so, then the shadow shader *only* runs on the second pass. But if you mean the right-hand images (drawing depth), then yes, I'm using different shaders for those.

Quote:Have you still got the active texture to be a FBO or PBuffer?


I'm actually using glCopyTexSubImage2D, not FBOs or pBuffers.

Quote:If your not needing texturing at the moment but a glDisable(GL_TEXTURE_2D) before your drawing code for your second pass and see it improves anything.


I'm not sure if I understand - I'm pretty sure I need the shadow map texture applied during the second pass, so that I can read from it in the fragment shader.

Thanks for the reply! Any other thoughts?
OK, I think I got it working. For those interested, there were two problems:

1. gluSphere seems to render its triangles with clockwise orientation, whereas everything else (it seems) is counterclockwise. For me, this didn't really matter much, since I probably won't be using this kind of sphere (it was just for testing).

2. I forgot/didn't realize that glTranslatef doesn't actually change the vertex coordinates, it just multiplies the modelview matrix. So in the vertex shader, gl_Vertex is *always* the coordinates given with glVertex*.

This is kinda frustrating, because I like using gluLookAt once, followed by glTranslatef to place my objects. So here's the workaround I developed (I'd be interested to hear other people's solutions):

Immediately after the gluLookAt call, we grab the modelview matrix, and compute the inverse:

float view[16];float eye[4];glGetFloatv(GL_MODELVIEW_MATRIX, view);// the inverse of the 3x3 rotation part is just the transposefor(int i=0;i<3;i++)	for(int j=0;j<i;j++)		std::swap(view[4 * i + j], view[4 * j + i]);// the inverse of the translation part is just the negativefor(int i=0;i<3;i++)	eye = -view[12 + i];eye[3] = 0;// and clear the translation part so that view is just a rotation matrixfor(int i=0;i<3;i++) {	view[4 * i + 3] = 0;	view[12 + i] = 0;}


Next, when we're setting up the texture matrix to send to the shader, we apply the above guys:

glMatrixMode(GL_TEXTURE);glLoadIdentity();glTranslatef(.5, .5, .5);glScalef(.5, .5, .5);glMultMatrixf(lightProjectionMatrix);glMultMatrixf(lightModelViewMatrix);glMultMatrixf(view);glTranslatef(eye[0], eye[1], eye[2]);


Finally, in the vertex shader, we need to temporarily move into eyespace

shadowTexCoord = gl_TextureMatrix[0] * gl_ModelViewMatrix * gl_Vertex;


Note what's happening here: since we don't know what sorts of translation/rotations we've done since the gluLookAt call, we can only be assured that gl_ModelViewMatrix * gl_Vertex is in eye-space. We then apply the inverse transformation of the *original* modelview matrix from that gluLookAt call; this gets us world-space coordinates. Finally, we can move to light-space and project like we'd expect.

This topic is closed to new replies.

Advertisement