Smooth shading: Problems with normals / lighting

Started by
11 comments, last by Martin 14 years, 5 months ago
I'm rendering a model with the glDrawElements function. Only using the normal and the vertex arrays for the moment. The vertex normals are drawn in white and seem to be correct. However the model is very weird looking. Has anyone got suggestions or idea's what could be wrong? Any help is appreciated. [Edited by - RvH on November 20, 2009 7:48:23 AM]
Advertisement
I can't see the pictures.
I can see the pictures. It is better to show some code. Also make sure your normals are of unit length.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);
I'm not 100% convinced that your normals do look right, hard to tell without a close up!

The 2nd picture is the most telling, if you look at the edge nearest the camera, the normals seems to be pointing inwards and some points, outwards at others and near lying in the plane of the triangle at other points. These errors would produce exactly the lighting effect you are seeing.

Additionally I'm not convinced all the normals are unit length, perhaps some are < 1 units long resulting in dark patches?
Cheers,MartinIf I've helped you, a rating++ would be appreciated
The normals at the toprow are indeed incorrect and some point inwards. But this is only in the toprow.


[Edited by - RvH on November 20, 2009 7:14:54 AM]
Your normalisation code in CalculateVertexNormals is wrong, you need to do a proper vector normalise, dividing by the number of contributing vertex normals is unfortunately not the same thing.

For example consider just 2 vertex normals {1,0,0} and {0,1,0}, if you add them together and divide by 2 you get {0.5,0.5,0} which is not normalised

sqrt(0.5^2 + 0.5^2 + 0^2) = 0.707107 != 1

As it happens the vertex normal should have been {0.707107, 0.707107, 0}

Its not entirely clear which vertices CalculateVertexNormals is referencing, I must admit I am having problems reading it. Perhaps the indexing could be clearer? I would expect the number of vertices referenced in building the normal to be different to the code you have. For example, an interior vertex might reference 9 vertices (this + 8 surrounding) or perhaps if you're interested in speed 5 vertices (this + diagonals or this + vertically + horizontally adjacent)

I would be tempted to build a function which generates a vertex for any X,Y coordinate on your grid, get that working then optimise later. This would be much clearer / easier to debug / easier to ask for help on.
Cheers,MartinIf I've helped you, a rating++ would be appreciated
I don't know OpenGL either (I'm a DirectX guy) but perhaps theres some issue with tristrips? Tristrips have to invert the winding order of each triangle for the purposes of backface culling. It almost looks like the vertex normal is being flipped per triangle but this really shouldn't happen....
Cheers,MartinIf I've helped you, a rating++ would be appreciated
So if I understand it correctly I need to normalize the vector after dividing it?

Sorry about the indexing, it is indeed very confusing. In pseudo code this is what Im trying to do:

      // Get the three triangles below the vertex      vec[0] = SurfaceNormals[ triIndex ];      vec[1] = SurfaceNormals[ indexPlusOne ];      vec[2] = SurfaceNormals[ indexPlusTwo ];      // Get the three triangles above the vertex      vec[3] = SurfaceNormals[ rowOffset + triIndex ];      vec[4] = SurfaceNormals[ rowOffset + indexPlusOne ];      vec[5] = SurfaceNormals[ rowOffset + indexPlusTwo ];      // Sum the vectors and then divide by 6 to average them      vertexNormal = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];      vertexNormal /= 6.0f; 


So instead of taking the surrounding vertices Im taking the normals of the surrounding faces.

For each vertex, sum the face normals that share that vertex and average the result. Then normalise. E.g. for a vertex in a mesh, it's going to share typically 6 triagles. Edge vertices will share less etc.

Another way to do it is to calculate the face normals as usual and add the result to each vertex in that triangle face (assuming the vertex normals have been initialized to (0,0,0)). At the end, loop through and normalize each vertex normal.
You don't need to divide the normal at all, just normalise it as is

So you're attempting to do this:
A   B---C   /|  /|  / | / | /  |/  |D---E---F|  /|  /| / | / |/  |/  G---H   I

Computing the normal for vertex E by considering summing the 6 triangles surface normals around E (and then normalising) should work fine. I'm not entirely sure this is what your pseudo code is doing however

I've previously found faster / more accurate / simpler ways of solving this problem without prestoring triangle normals. This method is independant of your actual triangulation used during rendering. (which is a good thing)
A   B   C   /|\    / | \  /  |  \ D---E---F \  |  /  \ | /   \|/  G   H   IBE = B-E;FE = F-E;HE = H-E;DE = D-E;VertexNormalE  = BE.Cross(FE);VertexNormalE += FE.Cross(HE);VertexNormalE += HE.Cross(DE);VertexNormalE += DE.Cross(BE);VertexNormalE.Normalise();

For more accuracy but at the cost of more processing you could even introduce A,C,G and I into the mix and use 8 cross products
A---B---C|\  |  /|| \ | / ||  \|/  |D---E---F|  /|\  || / | \ ||/  |  \|G---H---I
Cheers,MartinIf I've helped you, a rating++ would be appreciated

This topic is closed to new replies.

Advertisement