Sign in to follow this  

Problem calculating vertex normals...

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

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[i].numTriangles; j++) {
			vector1 = subMeshes[i].vertices[subMeshes[i].triangles[j].vertexIndices[2]].pos - subMeshes[i].vertices[subMeshes[i].triangles[j].vertexIndices[0]].pos;
			vector2 = subMeshes[i].vertices[subMeshes[i].triangles[j].vertexIndices[1]].pos - subMeshes[i].vertices[subMeshes[i].triangles[j].vertexIndices[0]].pos;

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

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

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

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

		for(int j = 0; j < subMeshes[i].numVertices; j++) 
			subMeshes[i].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();
}

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

for(int i = 0; i < numSubMeshes; i++)
{
//calculate and normalize triangle normals here, although it could be integrated in the next loop too

for(int k = 0; k < subMeshes[i].numTriangles; k++)
{
for(int j = 0; j < 3; ++j)
{
int index = subMeshes[i].triangles[k].vertexIndices[j];

subMeshes[i].vertices[index].normal += subMeshes[i].triangles[k].normal;
}
}

//normalize vertex normals here

}





Best regards!

Share this post


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

Share this post


Link to post
Share on other sites
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.511701
0 : - 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.511701
0 : - Vertex[1] -0.033499 0.858511 -0.511701
0 : - Vertex[2] -0.033499 0.858511 -0.511701
0 : - Vertex[3] -0.155364 0.656539 -0.738118
0 : - Vertex[4] -0.155364 0.656539 -0.738118
0 : - 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?

Share this post


Link to post
Share on other sites
Ah, I suspected something like this. Look at what you logged, 2 faces and 6 vertices. This means the triangles don't share any vertices. Each vertex is used by only one triangle, so when you add up all the triangle normals and take the average, you've just added up one triangle's normal. You'll need to make sure to get rid of duplicate vertices, then your code should work.

Share this post


Link to post
Share on other sites

Hi,

As mentioned, it seems that your faces don't share vertices. The reason may well be that they have different texture coordinates (not so probable) or another differing attribute.

Anyway, you'll need a way to handle this kind of data, and eliminating the double vertices may not be the solution since you'll still have to take care of the vertices with differing attributes.

Of course, it would be nicer that your models wouldn't need any pre-processing. So if there is a way to process the models offline or while exporting them from a program, that would be better solution.

To create smooth normals in this situation, you'll need to do a bit more work. You have to create a set of unique positional vertices and normals (ie. loop through the vertices and see which of them are (almost) in the same position). Each vertex should have an index to this set. While looping though the triangles, you'd add the face normal to the normal of this unique set. Then after processing, you'd fetch the normals to the vertices needed for rendering.

A long time ago I wrote an exporter to 3DS MAX and I did this step while exporting, of course it was simpler since I had the original mesh data available.

Hope this information was useful

Good luck!

Share this post


Link to post
Share on other sites
The solution was actually much simpler. All of my meshes are made up of sub-meshes which use a certain material defined in the .obj file (so they can be sorted by transparency/distance by the renderer). The way I had it set up the vertices were in each sub-mesh. I simply changed it so the mesh contains the vertices and the sub-mesh accesses vertex data from it's parent mesh. This way I don't have to check for duplicates. Now I have smooth shading (finally). Although I did introduce a new issue involving the uv's and another possible issue involving two faces of difference materials sharing a vertex :
But at least they will be new and difference problems :)

Thanks for the help.

Share this post


Link to post
Share on other sites

This topic is 3627 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.

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