Professional Documents
Culture Documents
Math - Calculating Face Normals
Math - Calculating Face Normals
Math - Calculating Face Normals
Any Face or 3D Surface has a Normal. This Normal determines the faces response to lighting within a 3D scene. The Normal is a Vector that points directly away from and is perpendicular to the face. If a light source is in line with the Normal then the surface will appear at its brightest. If the Normal points away from a light source then the surface will be rendered at its darkest. D C
Note: We need to perform the subtraction on each value in the vertex so Vector1.x = VertexB.x VertexA.x etc.
Vector 2
Vector 1
What we need for our Normal is an x,y,z Vector. For each of x,y and z you need to make the following calculations based on the values in the Vectors. CrossProduct.x = ( Vector1.y x Vector2.z ) - ( Vector1.z x Vector2.y ) CrossProduct.y = - ( ( Vector2.z x Vector1.x ) - ( Vector2.x x Vector1.z ) ) CrossProduct.z = ( Vector1.x x Vector2.y ) - ( Vector1.y x Vector2.x ) Below I have pasted the C++ code I use to calculate the Cross Product:
Normal.x = (v1.y * v2.z) - (v1.z * v2.y); Normal.y = -((v2.z * v1.x) - (v2.x * v1.z)); Normal.z = (v1.x * v2.y) - (v1.y * v2.x);
We have now calculated the Normal for our face/polygon. But are we done yet? No! Not quite. Because the Normal we have may not be the Normal we are looking for!
We normalize our normals with a little help from our good friend Pythagoras. We find the sum of the squares of our Cross Product and then find the square root of this value. This then gives us a Normalisation Factor which we can divide our cross product by in order to calculate the Unit Vector. Normalisation Factor = . 2 + . 2 + . 2
Normal.x = Normal.x / NormalisationFactor Normal.y = Normal.y / NormalisationFactor Normal.z = Normal.z / NormalisationFactor We can code this in C++ as:
CombinedSquares = (Normal.x * Normal.x) + (Normal.y * Normal.y) + (Normal.z * Normal.z); NormalisationFactor = sqrt(CombinedSquares); Normal.x = Normal.x / NormalisationFactor; Normal.y = Normal.y / NormalisationFactor; Normal.z = Normal.z / NormalisationFactor;
So now we have a Vector called Normal with values x, y, and z that are between -1 +1 that can be assigned to polygons when rendering.
void CalculateSurfaceNormal(FLOAT *Vector, bool NORMALIZE){ struct Vector3f{ FLOAT x,y,z; }Normal,v1,v2; FLOAT NormalisationFactor, CombinedSquares; v1.x = Vector[3] - Vector[0]; v1.y = Vector[4] - Vector[1]; v1.z = Vector[5] - Vector[2]; v2.x = Vector[6] - Vector[0]; v2.y = Vector[7] - Vector[1]; v2.z = Vector[8] - Vector[2]; Normal.x = (v1.y * v2.z) - (v1.z * v2.y); Normal.y = -((v2.z * v1.x) - (v2.x * v1.z)); Normal.z = (v1.x * v2.y) - (v1.y * v2.x); if(NORMALIZE){ CombinedSquares = (Normal.x * Normal.x) + (Normal.y * Normal.y) + (Normal.z * Normal.z); NormalisationFactor = sqrt(CombinedSquares); Vector[9] = Normal.x / NormalisationFactor; Vector[10] = Normal.y / NormalisationFactor; Vector[11] = Normal.z / NormalisationFactor; } else { Vector[9] = Normal.x; Vector[10] = Normal.y; Vector[11] = Normal.z; } return; }
When calling this function you need to pass a pointer to your FLOAT array and set NORMALIZE to TRUE if you want the Normal to be normalized. Written by: Richard Sabbarton Web: http://www.fullonsoftware.co.uk E-Mail: richard.sabbarton@gmail.com or richard@fullonsoftware.co.uk