Shadow volume rendering for md5meshes

Started by
4 comments, last by ma_hty 16 years ago
I have been trying to write a doom 3 model viewer in openGL and I am having trouble with shadow volumes. :( description of your image As you can see, the shadow is not rendered right. I'd appreciate any help on this.
Advertisement
I don't think anyone can help without further information. What technique are you using? What's your pseudo code?

This seems like a debugging problem, which is hard to analyse over the web, but possible. Perhaps post some of the code you have?
I am sorry about that.

I am using stencil shadow volumes. I calculate the silhouette edges of the md5mesh. I use the following edge structure to store the information.
class Edge{public:	int tri1;	int tri2;	int vert1;	int vert2;	// is it a silhouette edge ?	bool silhouette;	// 1st triangle front facing ?	bool facing1;	// 2nd triangle front facing ?	bool facing2;};


This is how the edges are detected.
//==============================================////==============================================void MD5Model::appendEdge(int meshnumber, int v1, int v2, int tri){	meshes[meshnumber]->edges.resize(meshes[meshnumber]->edges.size() + 1);	Edge* newedge = &meshes[meshnumber]->edges[meshes[meshnumber]->edges.size() - 1];	newedge->silhouette = 0;	// set the triangle index	newedge->tri1  = tri;	// set the edge vertices	newedge->vert1 = v1;	newedge->vert2 = v2;	// we don't have the second triangle yet !	newedge->tri2  = -1;}char s[1024];//==============================================////==============================================void MD5Model::findCreateMatchingEdge(int meshnumber, int v1, int v2, int tri){	for(unsigned int i = 0 ; i < meshes[meshnumber]->edges.size() ; i++)	{		if(meshes[meshnumber]->edges.vert1 == v2 && meshes[meshnumber]->edges.vert2 == v1)		{			if(meshes[meshnumber]->edges.tri2 == -1)			{				meshes[meshnumber]->edges.tri2 = tri;				return;			}			else			{				sprintf(s, "Edge with more than two tris: meshnumber, v1, v2, tri: %i %i %i %i\n", meshnumber, v1, v2, tri);				MessageBox(NULL, s, "Uh ??", MB_OK);			}		}	}	//we haven´t found a match for this edge, so we create it as a new one	appendEdge(meshnumber, v1, v2, tri);}//==============================================////==============================================void MD5Model::createEdgeList(int meshnumber){	for (int i = 0 ; i < meshes[meshnumber]->tris.size() ; i++)	{		Tri* tri = &meshes[meshnumber]->tris;				if(tri->v[0] < tri->v[1])			appendEdge(meshnumber, tri->v[0], tri->v[1], i);				if(tri->v[1] < tri->v[2])			appendEdge(meshnumber, tri->v[1], tri->v[2], i);		if(tri->v[2] < tri->v[0])			appendEdge(meshnumber, tri->v[2], tri->v[0], i);	}		for(int i = 0 ; i < meshes[meshnumber]->tris.size() ; i++)	{		Tri* tri = &meshes[meshnumber]->tris;				if(tri->v[0] > tri->v[1])			findCreateMatchingEdge(meshnumber, tri->v[0], tri->v[1], i);				if(tri->v[1] > tri->v[2])			findCreateMatchingEdge(meshnumber, tri->v[1], tri->v[2], i);				if(tri->v[2] > tri->v[0])			findCreateMatchingEdge(meshnumber, tri->v[2], tri->v[0], i);	}}


For each frame I calculate whether the triangles associated with an edge are facing the light or not.
//==============================================////==============================================void MD5Model::calculateSilhouetteEdges(float light_x, float light_y, float light_z, float light_w){	vecf4 light = vecf4(light_x, light_y, light_z, light_w);	for(size_t i = 0 ; i < 2 ; i++)	{		Mesh* mesh = meshes;		for(int j = 0 ; j < mesh->edges.size() ; j++)		{			mesh->edges[j].silhouette = 0;			mesh->edges[j].facing1 = 0;			mesh->edges[j].facing2 = 0;			// Extract normal of the first triangle connected to the edge			vecf4 n1 = vecf4(mesh->verts[mesh->tris[mesh->edges[j].tri1].v[0]].n[0],						     mesh->verts[mesh->tris[mesh->edges[j].tri1].v[0]].n[1],							 mesh->verts[mesh->tris[mesh->edges[j].tri1].v[0]].n[2],							 0.0f);			vecf4 n2;			if(mesh->edges[j].tri2 != -1)			{				n2 = vecf4(mesh->verts[mesh->tris[mesh->edges[j].tri2].v[0]].n[0],						   mesh->verts[mesh->tris[mesh->edges[j].tri2].v[0]].n[1],						   mesh->verts[mesh->tris[mesh->edges[j].tri2].v[0]].n[2],						   0.0f);			}						// bad naming !			// this normalizes the normals.			n1.normal();			n2.normal();			vecf4 pos =	vecf4((mesh->verts[mesh->edges[j].vert1].pos[0] + mesh->verts[mesh->edges[j].vert2].pos[0])/2.0f,							  (mesh->verts[mesh->edges[j].vert1].pos[1] + mesh->verts[mesh->edges[j].vert2].pos[1])/2.0f,							  (mesh->verts[mesh->edges[j].vert1].pos[2] + mesh->verts[mesh->edges[j].vert2].pos[2])/2.0f, 							  1.0f);						vecf4 lVec = pos - light;			lVec.normal();						float dot1 = lVec.dot3(n1);						if(mesh->edges[j].tri2 != -1)			{				float dot2 = lVec.dot3(n2);				if(dot1 >= 0.0f)					mesh->edges[j].facing1 = 1;				if(dot2 >= 0.0f)					mesh->edges[j].facing2 = 1;				if(mesh->edges[j].facing1 != mesh->edges[j].facing2)					mesh->edges[j].silhouette = 1;			}			else			{				if(dot1 >= 0.0f)				{					mesh->edges[j].silhouette = 1;					mesh->edges[j].facing1 = 1;				}			}		}	}}


I have been having trouble orienting the extruded quads in the right way. So I just use a flag (bool x) and render the quads based on the flag. I disable culling before rendering the quads.
//==============================================////==============================================void MD5Model::renderShadowVolumeQuads(float light_x, float light_y, float light_z, float light_w, bool x) {	calculateSilhouetteEdges(light_x, light_y, light_z, light_w);	glDisable(GL_LIGHTING);	glColor3f(1.0f, 1.0f, 1.0f);	for(size_t i = 0 ; i < 2 ; i++)	{		const Mesh* mesh = meshes;		for(int j = 0 ; j < mesh->edges.size() ; j++)		{			if(mesh->edges[j].facing1 != mesh->edges[j].facing2)			{				if(mesh->edges[j].facing1 != x)				{					glBegin(GL_QUADS);					glColor3f(1.0f, 0.0f, 0.5f);											glVertex3f(mesh->verts[mesh->edges[j].vert2].pos[0],								   mesh->verts[mesh->edges[j].vert2].pos[1],								   mesh->verts[mesh->edges[j].vert2].pos[2]);										glVertex3f(mesh->verts[mesh->edges[j].vert1].pos[0],								   mesh->verts[mesh->edges[j].vert1].pos[1],								   mesh->verts[mesh->edges[j].vert1].pos[2]);											glVertex4f(mesh->verts[mesh->edges[j].vert1].pos[0],								   mesh->verts[mesh->edges[j].vert1].pos[1],								   mesh->verts[mesh->edges[j].vert1].pos[2],								   0.0f);									glVertex4f(mesh->verts[mesh->edges[j].vert2].pos[0],								   mesh->verts[mesh->edges[j].vert2].pos[1],								   mesh->verts[mesh->edges[j].vert2].pos[2],								   0.0f);					glEnd();				}				else				{					glBegin(GL_QUADS);					glColor3f(0.0f, 0.0f, 0.5f);											glVertex3f(mesh->verts[mesh->edges[j].vert1].pos[0],								   mesh->verts[mesh->edges[j].vert1].pos[1],								   mesh->verts[mesh->edges[j].vert1].pos[2]);						glVertex3f(mesh->verts[mesh->edges[j].vert2].pos[0],								   mesh->verts[mesh->edges[j].vert2].pos[1],								   mesh->verts[mesh->edges[j].vert2].pos[2]);						glVertex4f(mesh->verts[mesh->edges[j].vert2].pos[0],								   mesh->verts[mesh->edges[j].vert2].pos[1],								   mesh->verts[mesh->edges[j].vert2].pos[2],								   0.2f);											glVertex4f(mesh->verts[mesh->edges[j].vert1].pos[0],								   mesh->verts[mesh->edges[j].vert1].pos[1],								   mesh->verts[mesh->edges[j].vert1].pos[2],								   0.2f);					glEnd();				}			}					}	}	glEnable(GL_LIGHTING);}
I'm not quite sure what is the direct causes of your problem.

However, there are something obviously wrong about your implementation.

Firstly, normalizing a four component vector not necessary give you the same thing as normalizing a three component vector. Therefore, these two lines are probably wrong
> vecf4 lVec = pos - light;
> lVec.normal();

Also, the silhouette determination algorithm work on face normal instead of vertex normal. If you give it vertex normal, it will simply break down.

Lastly, although you haven't reach that far yet, the rendering of shadow volume to stencil buffer can have problem due to capping. Please carefully check that you did the rendering to stencil buffer right.

I strongly recommend you to try the implementation in http://www.gamedev.net/reference/articles/article1990.asp, which is far better than what you are doing right now.
Thanks for replying.

> lVec.normal();

This function ignores the w-component. The result is a three component normalization.

I do use the normals of the faces for silhouette determination. I am still not sure what the problem is. Here is a picture of the silhouette rendered from the camera's view.

description of your image
Quote:Original post by k0d3r
Thanks for replying.
> lVec.normal();
This function ignores the w-component. The result is a three component normalization.


Mmmm...... as the comments in your source code, it is really a "bad naming".


Quote:Original post by k0d3r
I do use the faces of normals. I am still not sure what the problem is. The md5mesh contains winged edges, which I think is another problem.



Are you sure the following lines give you face normal?
> vecf4 n1 = vecf4(
> mesh->verts[mesh->tris[mesh->edges[j].tri1].v[0]].n[0],
> mesh->verts[mesh->tris[mesh->edges[j].tri1].v[0]].n[1],
> mesh->verts[mesh->tris[mesh->edges[j].tri1].v[0]].n[2],
> 0.0f);

By the way, although shadow volume algorithm can work on all triangular meshes, the optimization you used requires an extra assumption beside a triangular mesh, i.e. your 3D model must be closed. Therefore, each edge is shared by EXACTLY two triangles. Please make sure your mesh meet this requirement.

[Edited by - ma_hty on April 22, 2008 1:28:36 AM]

This topic is closed to new replies.

Advertisement