Sign in to follow this  
PiNtoS

shadow mapping problem (color-related)

Recommended Posts

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!

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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.)

Share this post


Link to post
Share on other sites
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 floor
float 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 ball
GLUquadric *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 box
float 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 box
glColor3f(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();

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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 transpose
for(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 negative
for(int i=0;i<3;i++)
eye[i] = -view[12 + i];
eye[3] = 0;

// and clear the translation part so that view is just a rotation matrix
for(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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this