• Advertisement
Sign in to follow this  

Problem With Shadow Volume Rendering

This topic is 4521 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everyone. I'm having a problem rendering Shadow Volumes using both the ZPass and the ZFail techniques. I can render the ZPass technique with no problem, but when I switch it to the ZFail technique, the problems start. I have a screen shot that shows you the problem that I get when I'm using zfail and the camera is inside the shadow volume. Image hosted by Photobucket.com ZFail Problem Image hosted by Photobucket.com ZPass Is Correct Image hosted by Photobucket.com ZFail Problem Thank you ahead of time for your assistance, it is very appreciated.

Share this post


Link to post
Share on other sites
Advertisement
Hi,

Looks to me like you aren't capping your volumes properly during the zfail.

It has been a while since I last looked at this [grin]
But you should render your front facing polygons (with respect to the light) and your back facing polygons (at infinity) as well as the shadow volume itself. Although, it may be that you render your front facing polygons with reversed winding order at infinity.

That failing, code (pseudo or otherwise) might help solve your problem [smile]

Regards,
ViLiO

Share this post


Link to post
Share on other sites
Thanks for replying. The code is quite long.

Here is my code for creating the shadow volume and rendering the shadow volume.


// Renders the shadow to the scene
void castShadow(ShadowedObject & object, GLfloat * lightPosition)
{
int reverseIt;

// Determine Which Faces Are Visible By The Light.
for (int i = 0; i < object.nFaces; i++) {
const Plane & plane = object.pFaces.planeEquation;

GLfloat side = plane.a * lightPosition[0] +
plane.b * lightPosition[1] +
plane.c * lightPosition[2] + plane.d;

if (side > 0)
object.pFaces.visible = true;
else
object.pFaces.visible = false;
}

// Preserve Attributes We Modify
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT
| GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT);

glDisable(GL_LIGHTING); // Turn Off Lighting
glDepthMask(GL_FALSE); // Turn Off Writing To The Depth-Buffer
glDepthFunc(GL_LESS);

glEnable(GL_STENCIL_TEST); // Turn On Stencil Buffer Testing
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Don't Draw Into The Colour Buffer
glStencilFunc(GL_ALWAYS, 0, ~0);
if (!guessMethod) {
reverseIt = useReverse;
} else {
reverseIt = LightPos[2] < ObjPos[2];
}
if (reverseIt) {
// First Pass. Increase Stencil Value In The Shadow
glFrontFace(GL_CW);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
doShadowPass(object, lightPosition);

// Second Pass. Decrease Stencil Value In The Shadow
glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
doShadowPass(object, lightPosition);

} else {
glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
doShadowPass(object, lightPosition);

glFrontFace(GL_CW);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
doShadowPass(object, lightPosition);
}
glFrontFace(GL_CCW);

glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // Enable rendering to colour buffer for all components

// Draw A Shadowing Rectangle Covering The Entire Screen
glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFFL);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glPushMatrix();
glLoadIdentity();
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(-0.1f, 0.1f, -0.10f);
glVertex3f(-0.1f, -0.1f, -0.10f);
glVertex3f(0.1f, 0.1f, -0.10f);
glVertex3f(0.1f, -0.1f, -0.10f);
glEnd();
glPopMatrix();
glDisable(GL_BLEND);
//glDisable(GL_STENCIL_TEST);
glPopAttrib();
}

// Creates shadow volume
void doShadowPass(ShadowedObject &object, GLfloat *lightPosition)
{
for (int i = 0; i < object.nFaces; i++)
{
const Face & face = object.pFaces;

if (face.visible)
{
// Go Through Each Edge
for (int j = 0; j < 3; j++)
{
int neighbourIndex = face.neighbourIndices[j];
// If There Is No Neighbour, Or Its Neighbouring Face
// Is Not Visible, Then This Edge Casts A Shadow
if (neighbourIndex == -1 || object.pFaces[neighbourIndex].visible == false)
{
// Get The Points On The Edge
const Point3f & v1 =
object.pVertices[face.vertexIndices[j]];
const Point3f & v2 =
object.pVertices[face.vertexIndices[(j + 1) % 3]];

// Calculate The Two Vertices In Distance
Point3f v3, v4;

v3.x = (v1.x - lightPosition[0]) * INFINITY;
v3.y = (v1.y - lightPosition[1]) * INFINITY;
v3.z = (v1.z - lightPosition[2]) * INFINITY;

v4.x = (v2.x - lightPosition[0]) * INFINITY;
v4.y = (v2.y - lightPosition[1]) * INFINITY;
v4.z = (v2.z - lightPosition[2]) * INFINITY;

// Draw The Quadrilateral (As A Triangle Strip)
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(v1.x, v1.y, v1.z);
glVertex3f(v1.x + v3.x, v1.y + v3.y, v1.z + v3.z);
glVertex3f(v2.x, v2.y, v2.z);
glVertex3f(v2.x + v4.x, v2.y + v4.y, v2.z + v4.z);
glEnd();
}
}
}
}
}



[Edited by - darrenc182 on October 8, 2005 7:24:27 PM]

Share this post


Link to post
Share on other sites
Hi again,

It'd probably be better if you just told me how you were building and rendering the zfail shadow volume in english ...for now [smile].

I think you have it set up fine you just aren't rendering enough during the zfail. Unlike zpass you have to cap the shadow volume at either end. I assume you are working out the shadow volume in the following way. If a face is pointing towards the light and it's neighbouring face isn't, then you have found an egde. That edge is then used to make a quad (comprising of the edge and the projected edge at infinity).

You can reuse your tests during the edge detection and render all the faces that were facing the light again as the near cap, and then project the front facing faces out to infinity and render them back to front (flip the normals). This provides the far cap.

Regards,
ViLiO

(edit: while I have a look at that code you should definately edit your post and put [ source ] [ /source ] tags around the code ...or else phantom will castrate you [lol])

Share this post


Link to post
Share on other sites
OK yeah that's how I'm finding the edges and creating the shadow volume. I posted my code above for rendering the shadow and creating the shadow volume. You can take a look at that if you like. I'm not sure how I would cap to infinity giving the current shadow volume rendering technique. I just put those tags on, thanks for the information.

Share this post


Link to post
Share on other sites
Well,

It seems you aren't rendering your caps [grin]

Hmm, ok well you will have to also loop through all your "visible" faces and render them during your doShadowPass function. Render them as normal and also render them projected to infinity and with the normals flipped. To do this is quite easy. You take the vertex position and and subtract the light position from it. Then draw the result at infinity using a vertex4f call where the w component is 0.0f.

Eg. from your shadow pass code
Point3f v3, v4;

v3.x = (v1.x - lightPosition[0]);
v3.y = (v1.y - lightPosition[1]);
v3.z = (v1.z - lightPosition[2]);

v4.x = (v2.x - lightPosition[0]);
v4.y = (v2.y - lightPosition[1]);
v4.z = (v2.z - lightPosition[2]);

// Draw The Quadrilateral (As A Triangle Strip)
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(v1.x, v1.y, v1.z);
glVertex4f(v3.x, v3.y, v3.z, 0.0f);
glVertex3f(v2.x, v2.y, v2.z);
glVertex4f(v4.x, v4.y, v4.z. 0.0f);
glEnd();


Share this post


Link to post
Share on other sites
Is it best for me to replace the code in my doShadow function with something representing your code or add that in addition to it, right after this:


// Draw The Quadrilateral (As A Triangle Strip)
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(v1.x, v1.y, v1.z);
glVertex3f(v1.x + v3.x, v1.y + v3.y, v1.z + v3.z);
glVertex3f(v2.x, v2.y, v2.z);
glVertex3f(v2.x + v4.x, v2.y + v4.y, v2.z + v4.z);
glEnd();





Do you mean loop through non visible sides as well because I am looping through visible sides already (if (face.visible))?

Share this post


Link to post
Share on other sites
void doShadowPass(ShadowedObject &object, GLfloat *lightPosition)
{
for (int i = 0; i < object.nFaces; i++)
{
const Face & face = object.pFaces;

if (face.visible)
{
// Go Through Each Edge
for (int j = 0; j < 3; j++)
{
int neighbourIndex = face.neighbourIndices[j];
// If There Is No Neighbour, Or Its Neighbouring Face
// Is Not Visible, Then This Edge Casts A Shadow
if (neighbourIndex == -1 || object.pFaces[neighbourIndex].visible == false)
{
// Get The Points On The Edge
const Point3f & v1 =
object.pVertices[face.vertexIndices[j]];
const Point3f & v2 =
object.pVertices[face.vertexIndices[(j + 1) % 3]];

// Calculate The Two Vertices In Distance
Point3f v3, v4;

v3.x = (v1.x - lightPosition[0]);
v3.y = (v1.y - lightPosition[1]);
v3.z = (v1.z - lightPosition[2]);

v4.x = (v2.x - lightPosition[0]);
v4.y = (v2.y - lightPosition[1]);
v4.z = (v2.z - lightPosition[2]);

// Draw The Quadrilateral (As A Triangle Strip)
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(v1.x, v1.y, v1.z);
glVertex4f(v3.x, v3.y, v3.z, 0.0f);
glVertex3f(v2.x, v2.y, v2.z);
glVertex4f(v4.x, v4.y, v4.z, 0.0f);
glEnd();
}
}
}
/*this face is visible so we need to use it for our caps

first off render the face as normal
then ...
each of the vertices of the face has to have the light position subtracted from it
just like points v3 and v4 above.
You then render the face again using the vertex position - the light position and instead of vertex3f
...you use vertex4f(x,y,z,0.0f) this draws the vertex at infinity (you dont need * INFITITY from above)
Also the second rendering of this face should be done in reverse (to flip the normal)
So the face is now back facing.*/

}
}




hehe I hope this clears things up [grin]

Regards,
ViLiO

(edit: this should make it perfectly clear at least why zfail is expensive and you should only use it when you have to ...use zfail at all other times [grin])

Share this post


Link to post
Share on other sites
OK thanks for all of the help. I will try to work through all the great information you gave me and see what happens from there. Would it be possible if I could get your contact information just in case I could ask you a few more questions later on? Once again thanks for all of the help.

Share this post


Link to post
Share on other sites
If you need to contact me just send me a private message through gamedev. If you do have further questions though you may as well bring them up in this topic ...so as the programmers of tomorrow can benefit from our discussion [grin]

Glad to help,
ViLiO

Share this post


Link to post
Share on other sites
Now when it comes to flipping the normals do I have to modify the vertices or just flip the normal data (ie. orginal normal: 0.0f,1.0f,0.0f new normal: 0.0f,-1.0f,0.0f)

Share this post


Link to post
Share on other sites
You reverse the vertex drawing order [smile]

(edit: this may be of some use. It was a conversion of a really simple zpass demo from codesampler I made a while ago. Just has one quad as a shadow caster [grin])

Share this post


Link to post
Share on other sites
do you mean change


glBegin(GL_TRIANGLE_STRIP);
glVertex4f(v1.x, v1.y, v1.z, 0.0f);
glVertex4f(v3.x, v3.y, v3.z, 0.0f);
glVertex4f(v2.x, v2.y, v2.z, 0.0f);
glVertex4f(v4.x, v4.y, v4.z, 0.0f);
glEnd();



to


glBegin(GL_TRIANGLE_STRIP);
glVertex4f(v4.x, v4.y, v4.z, 0.0f);
glVertex4f(v1.x, v1.y, v1.z, 0.0f);
glVertex4f(v3.x, v3.y, v3.z, 0.0f);
glVertex4f(v2.x, v2.y, v2.z, 0.0f);
glEnd();



Share this post


Link to post
Share on other sites
Maybe we should make this discussion real-time [grin]

Do you have irc? If so meet me in the #gamedev channel.

irc://irc.afternet.org/gamedev ...my nick will of course be ViLiO

If not ...then ...hmm I dunno[lol]

Share this post


Link to post
Share on other sites
I don't know about shadow volumes, but this is good comment. I was wondering, why are you using glVertex4f() function. You could use the 3f version just fine. I don't know whether it makes a difference speedwise, but if you have a lot of those statements, it might.

Share this post


Link to post
Share on other sites
Quote:
Original post by kburkhart84
I don't know about shadow volumes, but this is good comment. I was wondering, why are you using glVertex4f() function. You could use the 3f version just fine. I don't know whether it makes a difference speedwise, but if you have a lot of those statements, it might.


In glVertex4f the w component is needed to draw the vertex at infinity (which defaults to 1.0 in glVertex3f). If you don't project the volume out to infinity you get problems when the view frustum intersects the shadow volume. The call itself wouldn't cause any real overhead in terms of rendering speed (I am not overly familiar with the pipeline but if we are doing this in immediate mode, whether we use 3f or 4f ...it is still slow as [lol]).

Regards,
ViLiO

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement