Math - Calculating Face Normals

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 4

What is the Normal?

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

How to calculate the Normal for a surface


Each face is described by its vertices. A Vertex is a 3D Point at each corner of the polygon/face/surface. In the illustration above they are marked as a,b,c,etc. IMPORTANT NOTE: These points are arranged in an anti-clockwise order around the face. In order to calculate Normals this MUST ALWAYS be the case. Each Vertex contains 3 values. These are the x,y,z coodinates for that corner of the face. In order to calculate the Normal we need 3 vertices. For quads or any other irregular shaped faces you just need to choose 3 of them. The result will be the same because all points are on the same plane. We could use A,B,C OR A,B,D to calculate the normal for the quad above. I tend to use A,B,C. There are a few distinct steps required to calculate the Normal from these vertices.

STEP 1: Get the Vectors


The first thing we need to do is get the vectors which are marked in red above. The Vector is the movement from one point to the next. This is calculated by subtracting the values of Vertex A from the values of Vertex B. We need 2 Vectors to calculate the normal. The first Vector is from Point A to the first point around the face in an anti-clockwise direction, Point B and the second Vector is from Point A to the second point in an anti-clockwise direction. Point C. Vector 1 = Vertex B - Vertex A Vector 2 = Vertex C - Vertex A

Note: We need to perform the subtraction on each value in the vertex so Vector1.x = VertexB.x VertexA.x etc.

STEP 2: Calculating the Normal from the Vectors


Now that we have our Vectors we need to calculate the Normal of these vectors. We will write the Vectors as Vector1 and Vector2 and each vector has an x,y,z value so this will be Vector1.x and Vector2.y etc. To calculate the normal we need to calculate the Cross Product of these vectors. This will give us a point that is positioned perpendicular to the plane generated by the vectors. Cross Product

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!

STEP 3: Normalizing your Normals


Some rendering engines use the size or distance of the Normal to govern the brightness of the object/polygon when rendered. This will cause a problem because not all of your faces will be the same size. This effectively means that smaller objects will appear darker than bigger objects. Because of this we need to normalize the normals. By this I mean that we need to calculate the unit vector of the Normal.

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.

Example C++ Function


The following Code Example will calculate the correct Normal for a Polygon. Depending on your rendering engine and your existing code you may have various different data types for storing your Polygon information. For this reason I am passing the values of the Vertices to the function as a simple array of FLOAT values. There are 12 values in the array. The first 9 values should be the x, y and z values of your vertices and the last 3 will be populated with the Normal once it is calculated.

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

You might also like