Open GL Super Bible

Previous Table of Contents Next


Finding a Normal

Figure 9-13 presents another polygon that is not simply lying in one of the axis planes. The normal vector pointing away from this surface is more difficult to guess, so we need an easy way to calculate the normal for any arbitrary polygon in 3D coordinates.


Figure 9-13  A nontrivial normal problem

You can easily calculate the normal vector for any polygon consisting of at least three points that lie in a single plane (a flat polygon). Figure 9-14 shows three points, P1, P2, and P3, that you can use to define two vectors: vector V1 from P1 to P2, and vector V2 from P1 to P2. Mathematically, two vectors in three-dimensional space define a plane (your original polygon lies in this plane). If you take the cross product of those two vectors (written mathematically as V1 X V2, the resulting vector is perpendicular to that plane (or normal). Figure 9-15 shows the vector V3 derived by taking the cross product of V1 and V2.


Figure 9-14  Two vectors defined by three points on a plane


Figure 9-15  A normal vector as cross product of two vectors

Donít worry if you donít know how to take the cross product of two vectors; all you need is the function in Listing 9-3. To use this function, pass it an array containing any three vertices from your polygon (specify in counterclockwise winding order), and an array that will contain the normal vector on return. The constant values x, y, and z are provided for your benefit if you want to see how the function works.

Listing 9-3 Function to calculate a normal vector with any three vertices from a polygon

// Points p1, p2, & p3 specified in counterclockwise order
void calcNormal(float v[3][3], float out[3])
        {
        float v1[3],v2[3];
        static const int x = 0;
        static const int y = 1;
        static const int z = 2;

        // Calculate two vectors from the three points
        v1[x] = v[0][x] - v[1][x];
        v1[y] = v[0][y] - v[1][y];
        v1[z] = v[0][z] - v[1][z];

        v2[x] = v[1][x] - v[2][x];
        v2[y] = v[1][y] - v[2][y];
        v2[z] = v[1][z] - v[2][z];

        // Take the cross product of the two vectors to get
        // the normal vector which will be stored in out[]
        out[x] = v1[y]*v2[z] - v1[z]*v2[y];
        out[y] = v1[z]*v2[x] - v1[x]*v2[z];
        out[z] = v1[x]*v2[y] - v1[y]*v2[x];

        // Normalize the vector (shorten length to one)
        ReduceToUnit(out);
        }

Setting Up a Source

Now that you understand the requirements of setting up your polygons to receive and interact with a light source, itís time to turn on the lights! Listing 9-4 shows the SetupRC() function from the example program LITJET. Part of the setup process for this sample program creates a light source and places it to the upper-left, slightly behind the viewer. The light source GL_LIGHT0 has its ambient and diffuse components set to the intensities specified by the arrays ambientLight[], and diffuseLight[].This results in a moderate white light source.

GLfloat  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat  diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };
Ö
Ö
// Setup and enable light 0
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);

The light is positioned by this code:

GLfloat lightPos[] = { -50.f, 50.0f, 100.0f, 1.0f };
Ö
Ö
glLightfv(GL_LIGHT0,GL_POSITION,lightPos);

Here lightPos[] contains the position of the light. The last value in this array is 1.0, which specifies that the designated coordinates are the position of the light source. If the last value in the array is 0.0, it indicates that the light is an infinite distance away along the vector specified by this array. Weíll touch more on this later.

Finally, the light source GL_LIGHT0 is enabled:

glEnable(GL_LIGHT0);

Listing 9-4 Light and rendering context setup for LITJET

// This function does any needed initialization on the rendering
// context.  Here it sets up and initializes the lighting for
// the scene.
void SetupRC()
        {
        // Light values and coordinates
        GLfloat  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };
        GLfloat  diffuseLight[] = { 0.7f, 0.7f, 0.7f, 1.0f };
        Glfloat  lightPos[] = { -50.f, 50.0f, 100.0f, 1.0f };

        glEnable(GL_DEPTH_TEST);   // Hidden surface removal
        glFrontFace(GL_CCW);       // Counter clock-wise polygons face out
        glEnable(GL_CULL_FACE);    // Do not calculate inside of jet

        // Enable lighting
        glEnable(GL_LIGHTING);

        // Setup and enable light 0
        glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);
        glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseLight);
        glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
        glEnable(GL_LIGHT0);

        // Enable color tracking
        glEnable(GL_COLOR_MATERIAL);

        // Set Material properties to follow glColor values
        glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

        // Light blue background
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f );
        }

Setting the Material Properties

Notice in Listing 9-4 that color tracking is enabled, and the properties to be tracked are the ambient and diffuse reflective properties for the front surface of the polygons. This is just as it was defined in the AMBIENT sample program:

// Enable color tracking
glEnable(GL_COLOR_MATERIAL);

// Set Material properties to follow glColor values
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);


Previous Table of Contents Next