Smooth shading: Problems with normals / lighting
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]
I can see the pictures. It is better to show some code. Also make sure your normals are of unit length.
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?
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?
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]
[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.
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.
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....
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:
So instead of taking the surrounding vertices Im taking the normals of the surrounding faces.
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.
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:
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)
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
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
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement