Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

TomschikSimon

Shadow Volume Extrusion in a Vertex Program

This topic is 5281 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

I try to use the vertex program from www.paulsprojects.net for hardware shadow volume extrusion. But in my own programs the shadows produced by this vertex program looks like that if I use vertex normals:

If I use face normals I only see something like a shadow if I go through a wall:

The vertex program:

!!ARBvp1.0
# Vertex Program for shadow volume extrusion

# Parameters
PARAM mvp[4]		= { state.matrix.mvp };	#modelview projection matrix
PARAM lightPosition = program.env[0]; #object space light position

PARAM zero			= {0.0, 0.0, 0.0, 0.0};
PARAM one			= {1.0, 1.0, 1.0, 1.0};	
				#useful constants

# Per vertex inputs
ATTRIB iPos = vertex.position; #position
ATTRIB iNorm = vertex.normal; #normal

# Temporaries
TEMP lightVector; #light vector
TEMP lightDotNorm; #light vector dot normal
TEMP toSubtract; #What to subtract from the vertex position
TEMP position; #Computed position (object space)
TEMP toMult; #What to multiply position by to set w=0

# Outputs
OUTPUT oPos = result.position; #position

# Calculate light vector
SUB lightVector, lightPosition, iPos;

# lightDotNorm = light vector dot normal(object space)
DP3 lightDotNorm, lightVector, iNorm;

# toSubtract= (1, 1, 1, 0) if l.n<0
#			= (0, 0, 0, 0) if l.n>0
MOV toSubtract, zero;
SLT toSubtract.xyz, lightDotNorm, zero;

# toSubtract= (Lx, Ly, Lz, 0)	if l.n<0 (light position)
#			= (0, 0, 0, 0)		if l.n>0
MUL toSubtract, toSubtract, lightPosition;

# position  = (Vx-Lx, Vy-Ly, Vz-Lz, Vw) if l.n<0
#			= (Vx, Vy, Vz, Vw)			if l.n>0
MOV position, iPos;
SUB position, position, toSubtract;

# toMult= (1, 1, 1, 0) if l.n<0
#		= (1, 1, 1, 1) if l.n>0
MOV toMult, one;
SGE toMult.w, lightDotNorm, zero;

# position  = (Vx-Lx, Vy-Ly, Vz-Lz, 0) if l.n<0
#			= (Vx, Vy, Vz, Vw)			if l.n>0
MUL position, position, toMult;

# project to clip coords
DP4 oPos.x, mvp[0], position;
DP4 oPos.y, mvp[1], position;
DP4 oPos.z, mvp[2], position;
DP4 oPos.w, mvp[3], position;

END

Because the vertex program is working in the demo from www.paulsprojects.net I think the problem must be the normal calculation in my demo.

Face normals:

VECTOR3D vVector1, vVector2, vNormal, vPoly[3];

VECTOR3D *pTempNormals	= new VECTOR3D [m_Header.numTriangles];
	
// Go though all of the faces of this object

for(int j=0; j < numFaces; j++)
{												
// To cut down LARGE code, we extract the 3 points of this face

vPoly[0] = Verts[j*3];
vPoly[1] = Verts[j*3+1];
vPoly[2] = Verts[j*3+2];

// Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)


vVector1 = vPoly[1] - vPoly[0];		// Get the vector of the polygon 

vVector2 = vPoly[2] - vPoly[0];		// Get a second vector of the polygon


vNormal  = vVector2.CrossProduct(vVector1);	// Return the cross product of the 2 vectors

						//(normalize vector, but not a unit vector)

pNormals[j] = vNormal;
pNormals[j].Normalize();

Vertex normals:

VECTOR3D vVector1, vVector2, vNormal, vPoly[3];

VECTOR3D *pTempNormals	= new VECTOR3D [m_Header.numTriangles];
	
// Go though all of the faces of this object

for(int j=0; j < numFaces; j++)
{												
// To cut down LARGE code, we extract the 3 points of this face

vPoly[0] = Verts[j*3];
vPoly[1] = Verts[j*3+1];
vPoly[2] = Verts[j*3+2];

// Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)


vVector1 = vPoly[1] - vPoly[0];		// Get the vector of the polygon 

vVector2 = vPoly[2] - vPoly[0];		// Get a second vector of the polygon


vNormal  = vVector2.CrossProduct(vVector1);	// Return the cross product of the 2 vectors

						//(normalize vector, but not a unit vector)

// Save the un-normalized normal for the vertex normals

pTempNormals[j] = vNormal;

//////////////// Now Get The Vertex Normals /////////////////

VECTOR3D vSum;
vSum.LoadZero();				// vSum =(0,0,0)

GLuint shared=0;
for (int z = 0; z < numVertices; z++)			// Go through all of the vertices

{
	for (GLuint j = 0; j < numFaces; j++)		// Go through all of the triangles

	{					// Check if the vertex is shared by another face

		if (Verts[j*3+0] == Verts[z] ||
		    Verts[j*3+1] == Verts[z] ||
		    Verts[j*3+2] == Verts[z])
		{
			vSum = vSum + pTempNormals[j];
			// Add the un-normalized normal of the shared face

			shared++;
			// Increase the number of shared triangles

		}
	}      
	// Get the normal by dividing the sum by the shared.

	// We negate the shared so it has the normals pointing out.

	pNormals[z] = vSum / GLfloat(-shared);

	// Normalize the normal for the final vertex normal

	pNormals[z].Normalize();
	vSum.LoadZero();			// Reset the sum

	shared = 0;				// Reset the shared

}

I don't know what is wrong with this code and I hope somebody knows a solution

[edited by - TomschikSimon on June 3, 2004 2:00:28 AM]

Share this post


Link to post
Share on other sites
Advertisement
A bit of advice when debugging stuff like this:

1.) try a simpler model.
2.) render everything that you calculate to check visually that it''s working.

So, for (1) above, try something like a cube. For (2), try turn off the fancy stencilling techniques and simply render the faces of the volume. Also, try render them with glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) (wireframe). See if anything is screwing up. If you are still getting things wrong, try render the normals on your faces (and vertices) to make sure they look alright.

Another catchy bit when doing the front-face/back-face check: it is not enough to just dot-product the face normal with the camera direction since the face may still be visible even if it is more than 90 degrees (due to the perspective of the camera). If this is your problem, you may have to dotproduct the vector from the camera to a vertex on the face with the face normal. This will almost certainly give you the correct result.

Also, I don''t know whether you are doing the stencilling correctly. Do you check whether a face is facing the camera and then add (or subtract) to/from the stencil buffer based on that for each face (as per the stencil shadow method)? The vertex-program looks okay, but how are you actually submitting your vertices?

Hope I''ve mentioned something that helps.



Share this post


Link to post
Share on other sites

Thank you for your advice. Models like cubes and spheres are working without any problem. Even some more complex models are working:



but only some:



The yellow lines are the vertex-normals, but I can't see any wrong normal.



However the shadow volume seems to be OK




My drawing code:



// Enable client states

glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

// Set the array-pointers for drawing (using an ATI-Extension)

glArrayObjectATI(GL_NORMAL_ARRAY, 3, GL_FLOAT, 0, g_uiNormalObject, 0);
glArrayObjectATI(GL_VERTEX_ARRAY, 3, GL_FLOAT, 0, g_uiVertexObject, 0);

// choose vertex-program

ShadowVolumeExtrusion.select();
glEnable(GL_VERTEX_PROGRAM_ARB);

glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 0,
// light-position for vertex program (object space)

lightPos.x,lightPos.y,lightPos.z, 1.0f);

// Prepare for drawing the shadow

glPushAttrib(GL_ALL_ATTRIB_BITS);
glDisable(GL_LIGHTING);
glDisable(GL_FOG);
glDepthMask(GL_FALSE);

glColorMask(0, 0, 0, 0);
glEnable(GL_CULL_FACE);

glEnable(GL_STENCIL_TEST);

glStencilFunc(GL_ALWAYS, 0, ~0);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
glCullFace(GL_FRONT);

// Draw model with culling frontside polygons

glDrawArrays(GL_TRIANGLES, 0, numOfFaces*3);

glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
glCullFace(GL_BACK);

// Draw model with culling backside polygons

glDrawArrays(GL_TRIANGLES, 0, numOfFaces*3);

glPopAttrib();

// disable vertex program

glDisable(GL_VERTEX_PROGRAM_ARB);

// Draw a dark rect which covers the whole screen where the stencil is not equal to 0

//(I don't write the code here because it is working well)


// Draw model

m_Texture[0].select(); // choose texture for model

glCullFace(GL_BACK);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Set the array-pointer for drawing (using an ATI-Extension)

glArrayObjectATI(GL_TEXTURE_COORD_ARRAY, 2, GL_FLOAT, 0, g_uiTexCoordObject, 0);

glDrawArrays(GL_TRIANGLES, 0, numOfFaces*3);

// Disable client states

glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

// Draw vertex-normals

glDisable(GL_TEXTURE_2D);
glBegin (GL_LINES);
for(int i=0; i<numOfFaces*3; i++)
{
glColor3ub(255, 255, 0);
// Make the normal line YELLOW

glVertex3fv(pVerts[i]);
// draw the normal on the vertice

glVertex3f(pVerts[i].x+pNormals[i].x, pVerts[i].y+pNormals[i].y, pVerts[i].z+pNormals[i].z);
}
glEnd();


[edited by - TomschikSimon on June 4, 2004 5:57:32 AM]

Share this post


Link to post
Share on other sites
Hi;
I had stencil shadows in my engine, and i found some problems like yours (even im doing extrusion in CPU)...i''ve minimized it eliminating repeated vertices, and checking if any normal has a lenght = 0 ... it occurs when we have animated models and have to calculate the faces normal each frame...if u want the code above just ask me

Share this post


Link to post
Share on other sites
Without looking at the code the screenshot gives it away ...

Your problem is the models, they have cracks in them making the shadow volumes freak out, you need a solid "closed" mesh to make shadows work properly

I hope this helps

Share this post


Link to post
Share on other sites
Thank you for your answers. Is there any possibility to make a "closed" model out of one which isn't closed? Perhaps somebody knows a program or source code that could do this.

[edited by - TomschikSimon on June 5, 2004 4:39:50 AM]

Share this post


Link to post
Share on other sites
[idea that just came to my mind and it may not work]

What about for each vertex in your model checking if there is another vertex near to it and if so, then "connecting" the two vertices (f.e. by creating a new vertex at the middle of the other two) and throwing the one (or both of them if you create a new one) away. Having a #defined value for the minimum distance of two vertices will help you decided which is the best.

You may do that at the model load part of your engine or write a separate program that does the work.

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!