Problem calculating vertex normals...

Started by
11 comments, last by c_olin 16 years, 3 months ago
When I calculate and use my mesh's vertex normals my mesh gets rendered with flat shading. Same exact results as if I use face normals. I'm using the CML library for my vector structures and math. The problem might be in how I'm using CML so I have also posted on the CML forums. But I was wondering if you guys could give it a quick look and see if I'm making a stupid mistake somewhere... Here is my code for calculating both face and vertex normals:

void Mesh::computeNormals() {
	Vector3f vector1 = Vector3f(0, 0, 0);
	Vector3f vector2 = Vector3f(0, 0, 0);
	Vector3f sum     = Vector3f(0, 0, 0);
	int shared       = 0;

	for(int i = 0; i < numSubMeshes; i++) {
		// Face Normals.
		for(int j = 0; j < subMeshes.numTriangles; j++) {
			vector1 = subMeshes.vertices[subMeshes.triangles[j].vertexIndices[2]].pos - subMeshes.vertices[subMeshes.triangles[j].vertexIndices[0]].pos;
			vector2 = subMeshes.vertices[subMeshes.triangles[j].vertexIndices[1]].pos - subMeshes.vertices[subMeshes.triangles[j].vertexIndices[0]].pos;

			subMeshes.triangles[j].normal = cml::cross(vector2, vector1);
		}

		// Vertex Normals.
		shared = 0;
		sum.set(0, 0, 0);
		for(int j = 0; j < subMeshes.numVertices; j++) {
			for(int k = 0; k < subMeshes.numTriangles; k++) {
				if(subMeshes.triangles[k].vertexIndices[0] == j ||
				   subMeshes.triangles[k].vertexIndices[1] == j ||
				   subMeshes.triangles[k].vertexIndices[2] == j) {
					sum += subMeshes.triangles[k].normal;
					shared++;
				}
			}
			
			subMeshes.vertices[j].normal = sum/(float)shared;

			shared = 0;
			sum.set(0, 0, 0);			
		}

		// Normalize Both normals.
		for(int j = 0; j < subMeshes.numTriangles; j++) 
			subMeshes.triangles[j].normal.normalize();

		for(int j = 0; j < subMeshes.numVertices; j++) 
			subMeshes.vertices[j].normal.normalize();
	}
}

And here for rendering (OpenGL):

void Renderer::preRenderSubMesh(const SubMesh& subMesh) {
	glBegin(GL_TRIANGLES);
	for(int j = 0; j < subMesh.numTriangles; j++) {
		//glNormal3fv(subMesh.triangles[j].normal.data());				   
		for(int k = 0; k < 3; k++) {
			glNormal3fv(subMesh.vertices[subMesh.triangles[j].vertexIndices[k]].normal.data());				   
			glTexCoord2fv(subMesh.vertices[subMesh.triangles[j].vertexIndices[k]].uv.data());	
			glVertex3fv(subMesh.vertices[subMesh.triangles[j].vertexIndices[k]].pos.data());
		}
	}
	glEnd();
}

Advertisement
It looks like you have the right idea. One thing I would try would be to normalize the triangle face normals before you use them to calculate the vertex normals.

Otherwise, your bug is not obvious to me. If you set the vertex normals to something obviously wrong (perhaps set them all to (0, 1, 0) or something) does it show up obviously wrong, or does it still render with flat shading? This could tell you if the bug is somewhere besides your normal calculation.
Setting the vertex normals to (0, 1, 0) definitely makes a difference. It makes the entire object completely lit.

Yeah I've been looking over this code and trying everything for a couple days now. It's really stumping me.
What if in computeNormals() you randomly all vertex normals to either (0 1 0) or (0 -1 0), chosen randomly. Do you get any smooth shading in that case, or does the shading look like it's on a per polygon basis (some light up, some completely dark, no smooth interpolation between)?

It seems really weird to me that you can still have flat shading when you are setting the normal on a per vertex basis in the draw function.

This may sound like a stupid question, but are you sure each submesh has more than one triangle in it? Vertices won't be able to see triangles in different submeshes.
I am not certain if this is related to your problem, but I think the way you calculate your vertex normals is not fully correct.

From what I understand of your code a vertex normal is the sum of all the face normals holding the vertex.

But you may want to only consider face normals that are not collinear.

For example, let's consider a box. Each corner is connected to 3 sides, but since your are dealing with triangles, each side is composed of 2 faces. Now depending on how your box is constructed, for each of the 3 sides a corner is connected to, a side will either be 2 collinear faces or only 1.
Also, try drawing the normals as lines going out from your vertices. You will see if they look like expected.
Hi,

Have you set your output to be gouraud shaded? Flat shading might be default setting on some hardware. Also, in theory it is possible that the triangles don't share any vertices for several reasons such as different vertex coordinates or differing smoothing groups. This would lead naturally to flat shaded output.

//you could use the following loop to calculate the tri normals, it does the //same thing as yours, but in a more efficient mannerfor(int i = 0; i < numSubMeshes; i++){//calculate and normalize triangle normals here, although it could be integrated in the next loop toofor(int k = 0; k < subMeshes.numTriangles; k++) {   for(int j = 0; j < 3; ++j)   {   int index = subMeshes.triangles[k].vertexIndices[j];   subMeshes.vertices[index].normal += subMeshes.triangles[k].normal;   }}   //normalize vertex normals here}


Best regards!
Yes. I have "glShadeModel(GL_SMOOTH);" set. And I tried your code kuana, and it does the same thing. Flat shaded surface... This is getting really frustrating.
New development...

I slapped some logger calls in the calculation function and here is a part of the output...

// First 2 faces...0 : - Face[0] -0.033499 0.858511 -0.5117010 : - Face[1] -0.155364 0.656539 -0.738118// Vertices that are part of the first two faces...0 : - Vertex[0] -0.033499 0.858511 -0.5117010 : - Vertex[1] -0.033499 0.858511 -0.5117010 : - Vertex[2] -0.033499 0.858511 -0.5117010 : - Vertex[3] -0.155364 0.656539 -0.7381180 : - Vertex[4] -0.155364 0.656539 -0.7381180 : - Vertex[5] -0.155364 0.656539 -0.738118


Obviously you can see why I'm getting flat shading. The vertex normals are the exact same as the faces they are used by. But the question is... why does this happen?

I don't get it... does anyone else have any ideas?
Ok... I think that the problem is in my loading code... I'm going to take a look at that..

This topic is closed to new replies.

Advertisement