Math - Calculating Face Normals

Embed Size (px)

Citation preview

  • 8/6/2019 Math - Calculating Face Normals

    1/4

    What is the Normal?

    Any Face or 3D Surface has a Normal. This Normal determines the faces response to lighting withina 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.

    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 orderto 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

    A B

    C

    A B

    CD

  • 8/6/2019 Math - Calculating Face Normals

    2/4

    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 VectorsNow 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.

    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.

    Cross Product

    Vector 2

    Vector 1

  • 8/6/2019 Math - Calculating Face Normals

    3/4

    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.

  • 8/6/2019 Math - Calculating Face Normals

    4/4

    Example C++ FunctionThe 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, yand 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.ukE-Mail:[email protected]@fullonsoftware.co.uk

    http://www.fullonsoftware.co.uk/http://www.fullonsoftware.co.uk/http://www.fullonsoftware.co.uk/mailto:[email protected]:[email protected]:[email protected]:[email protected]:[email protected]:[email protected]:[email protected]:[email protected]://www.fullonsoftware.co.uk/