Cover page images (elite)

Laboratorio Grafica al Calcolatore 2009/2010: Lecture 7 - Lighting

Roberto Toldo, <nome.cognome@univr.it >




Hit the space bar for next slide

Previous Lecture Summary

Viewing Transformations

In previous sections, we discussed projection transformations. In particular, we described the viewing volume, either a rectangular box or a pyramid-shaped viewing frustum. This section describes how to move the viewing volume to see the world from different points of view.

To use the camera analogy, the projection transformation describes the type of lens to use. That camera is fixed onto a tripod. The viewing transformation describes where you put the tripod in the world and where you choose to point it.

The viewing transformation commands, polarView and gluLookAt, are commands which compose their transformation on the current matrix stack. They should be used when the ModelView matrix stack is active.

The Default Viewing Transformation

By default, the `camera' (center of projection) is positioned at the origin, (0.0, 0.0, 0.0) and pointed down the negative half of the z axis. The camera is not `twisted' (tilted).

Let's try an exercise to visualize this. This is really an exercise; body movements and all. Stick your right hand straight out to your right side (parallel to the ground). If you are looking north, your right hand should be pointing east. Your right hand is the x axis. Now raise your left hand over your head. Your left hand forms the y axis. Now pretend that you are a camera. Your head is at the origin of the world, (0.0, 0.0, 0.0). You are now looking down the negative z axis. Imagine the numbers: -1.0, -2.0, -3.0, in front of your face. Behind your head are the numbers: 1.0, 2.0, 3.0. You see a viewing volume that is in the default viewing position.

The gluLookAt Viewing Transformation

There are nine parameters for the gluLookat utility command. The first three (eyex, eyey, eyez) define the location for the viewpoint, or in other words, "Where you put the camera." The next three parameters (centerx, centery, centerz) define a 3D reference point, or in other words, "Where you aim the camera." The viewpoint and the reference point define the line of sight. Finally, the last three parameters (upx, upy, upz) define the up vector, or in other words, "Which way is up", relative to the camera.

The polarView Viewing Transformation

An alternative to the gluLookat utility command is to write your own viewing transformation. A useful viewing transformation to create is one that simulates the IRIS GL polarview() command.

void
polarView( GLfloat distance, GLfloat azimuth, 
        GLfloat incidence, GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

There are four parameters for this polarView command: distance, azimuth, incidence and twist. The first three parameters, distance, azimuth, and incidence, move the viewpoint. distance is the distance (a floating point value) from the origin. azimuth is the azimuthal angle in the x, y plane, measured counterclockwise from the y axis. incidence is the incidence angle in the y, z plane, measured counterclockwise from the z axis. Finally, twist rotates, or tilts, the viewpoint counterclockwise around the line of sight. Each angle is expressed in degrees.

Summary

Setting up a scene in 3D is something like taking a photograph. The first step in taking a photo is to select a lens for the camera. In effect, by selecting a lens, you are choosing how much of the world to view, or setting up a viewing volume. This is analogous to giving a projection transformation in a program. After selecting the proper lens, you position the camera (or the eye) somewhere in space, and you decide where to point your camera (point your line of sight). In a 3D program, this is the same as doing a viewing transformation. Finally, in taking a photo, you position the objects you wish to photograph somewhere in the scene. This is analogous to a modeling transformation.

Lecture Objectives

At the completion of this lecture, you will be able to

Notes:

Up to this point, most of our objects have looked flat, even though they have been rendered in 3D. Lighting is what gives most objects their 3D appearance.

OpenGL approximates light and lighting as if light can be broken into separate red, green and blue components. The color of a light is characterized by the amount of R, G and B it emits.

Material properties represent the percent of incoming RGB that an object reflects. The actual color of an object depends on its material properties, and the color of the light sources. For example, a magenta ball reflects incoming red and blue light, but will appear red if lit only by red light, or blue if lit only by blue light.

Lighting Basics

To perform basic lighting, you will need to

Enabling Lighting Calculations

GLvoid glEnable( GLenum option )

GLvoid glDisable( GLenum option )

Turning On the Lights

GLvoid glEnable( GLenum option )

GLvoid glDisable( GLenum option ) 

Notes:

The value <n> can be from 0 to the number of supported lights minus one. An OpenGL implementation is required to support at least eight lights, but can have more. To find the number of lights an implementation supports use:

   glGetIntegerv( GL_MAX_LIGHTS, &maxLights );

It is a common error to forget to turn on a specific light. If your scene is unexpectedly dark, check to see if you have enabled a light.

Adding a light is expensive, so use no more than necessary.

What Is a Normal Vector?

Imagine that you have a small mirror positioned at your vertex; a normal specifies which direction the face of the mirror is pointing

Notes:

In more technical terms, a normal is a vector that points in the direction perpendicular to the tangent of a surface.

For some objects, finding the normal is very straightforward.

Specifying Normals

GLvoid glNormal3f( GLfloat nx, GLfloat ny, GLfloat nz ) 

Notes:

Scalar and vector forms of glNormal3*()

Scalar Forms Vector Forms
glNormal3b( nx, ny, nz ) glNormal3bv( v )
glNormal3s( nx, ny, nz ) glNormal3sv( v )
glNormal3i( nx, ny, nz ) glNormal3iv( v )
glNormal3f( nx, ny, nz ) glNormal3fv( v )
glNormal3d( nx, ny, nz ) glNormal3dv( v )

Specifying Face Normals

If every vertex in a primitive has the same normal, it is called a face normal

Notes:

If you know the vertices of a primitive in your model, you can compute the face normal using the vector cross product. This method only works if your primitive is planar.

Triangle vertices are always coplanar, so they make good primitives for tessellated surfaces.

If you are rendering many flat-shaded, lit polygons, set glShadeModel(GL_FLAT) for better performance.

Specifying Vertex Normals

If every vertex in a primitive has a different normal, they are called vertex normals

Automatic Normalization of Normal Vectors

You can tell OpenGL to automatically normalize the normal vectors that you specify in your program

GLvoid glEnable( GLenum option )

GLvoid glDisable( GLenum option )

Notes:

When automatic normalization is enabled, OpenGL normalizes every normal vector whether or not it is already unit length. It is better to do your own normalization where it is needed; otherwise, OpenGL spends time normalizing everything (even things that are already normalized).

In many cases, GL_RESCALE_NORMAL can operate faster than renormalization, while resulting in the same unit normals. Rescaling may be used when the ModelView has been rotated or uniformly scaled. Do not use rescaling if there are non-uniform scales in the ModelView; the results will be incorrect.

Whenever possible, use unit length normal vectors and avoid using glScale*() with lighting.

Example: normals.c

/* normals.c - Show the effect of lighting on a flat surface
 *     when using face normals and vertex normals. The red 
 *     lines represent the directions of the normal vectors.
 *
 *     Left Mousebutton        - move eye position
 *     Middle Mousebutton      - change twist angle
 *     Right Mousebutton       - move up / down to zoom in / out
 *     Escape Key              - exit the program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include "3d.h" 

#include <math.h>
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  drawScene( GLvoid );
GLvoid  reshape( GLsizei, GLsizei );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  mouse( GLint, GLint, GLint, GLint );
GLvoid  motion( GLint, GLint );

GLvoid  drawNormal( GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat );

void resetView( GLvoid );
void polarView( GLfloat, GLfloat, GLfloat, GLfloat );
void printHelp( char * );

/* Global Definitions */

#define KEY_ESC     27      /* ascii value for the escape key */

/* Global Variables */

enum            actions { MOVE_EYE, TWIST_EYE, ZOOM, MOVE_NONE };
static GLint    action;

static GLdouble     xStart = 0.0, yStart = 0.0;

static GLfloat      fovy, nearClip, farClip, distance, twistAngle, incAngle, azimAngle;

void
main( int argc, char *argv[] )
{
    GLsizei width, height;

    glutInit( &argc, argv );

    /* create a window that is 1/4 the size of the screen */

    width = glutGet( GLUT_SCREEN_WIDTH ); 
    height = glutGet( GLUT_SCREEN_HEIGHT );
    glutInitWindowPosition( width / 4, height / 4 );
    glutInitWindowSize( (width / 2) - 4, height / 2 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initgfx();

    glutMouseFunc( mouse );
    glutMotionFunc( motion );
    glutKeyboardFunc( keyboard );
    glutReshapeFunc( reshape );
    glutDisplayFunc( drawScene ); 

    printHelp( argv[0] );

    glutMainLoop();
}

void
printHelp( char *progname )
{
    fprintf(stdout, "\n%s - demonstrate face and vertex normals\n\n" 
            "Left Mousebutton    - move eye position\n"
            "Middle Mousebutton     - change twist angle\n"
            "Right Mousebutton      - move up / down to zoom in / out\n"
            "Escape Key             - exit the program\n\n",
            progname);
}

GLvoid
initgfx( GLvoid )
{
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glEnable( GL_DEPTH_TEST );

    fovy = 60.0;    /* field of view in Y */
    nearClip = 3.0;     /* Near clipping plane location */
    farClip  = 12.0;    /* Far clipping plane location */

    resetView();

    /* Turn on a default light */
    glEnable( GL_LIGHT0 );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
    switch (key) {
    case KEY_ESC:   /* Exit whenever the Escape key is pressed */
         exit(0);
    }
}

GLvoid 
mouse( GLint button, GLint state, GLint x, GLint y )
{
    static GLint buttons_down = 0;

    if (state == GLUT_DOWN) {
         switch (button) {
         case GLUT_LEFT_BUTTON:
              action = MOVE_EYE;
              break;
         case GLUT_MIDDLE_BUTTON:
              action = TWIST_EYE;
              break;
         case GLUT_RIGHT_BUTTON:
              action = ZOOM;
              break;
         }

         /* Update the saved mouse position */
         xStart = x;
         yStart = y;
    } else {
         if (--buttons_down == 0)
              action = MOVE_NONE;
    }
}

GLvoid
motion( GLint x, GLint y )
{
    switch (action) {
    case MOVE_EYE:
         /* Adjust the eye position based on the mouse position */
         azimAngle += (GLdouble) (x - xStart);
         incAngle -= (GLdouble) (y - yStart);
         break;
    case TWIST_EYE:
         /* Adjust the eye twist based on the mouse position */
         twistAngle = fmod(twistAngle+(x - xStart), 360.0);
         break;
    case ZOOM:
         /* Adjust the eye distance based on the mouse position */
         distance -= (GLdouble) (y - yStart)/10.0;
         break;
    default:
         printf("unknown action %d\n", action);
    }
    
    /* Update the stored mouse position for later use */
    xStart = x;
    yStart = y;

    glutPostRedisplay();
}

void
resetView( GLvoid )
{
    distance = nearClip + (farClip - nearClip) / 2.0;
    twistAngle = 0.0;    /* rotation of viewing volume (camera) */
    incAngle = 60.0;
    azimAngle = 0.0;
    fovy = 60.0;         /* Field of view in Y angle */
}

GLvoid
reshape( GLsizei width, GLsizei height )
{
    GLdouble     aspect;

    glViewport( 0, 0, width, height );

    aspect = (GLdouble) width / (GLdouble) height;

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( fovy, aspect, nearClip, farClip );
    glMatrixMode( GL_MODELVIEW );
}

void
polarView( GLfloat distance, GLfloat azimuth, GLfloat incidence,
              GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

GLvoid
drawNormal( GLfloat px, GLfloat py, GLfloat pz,
    GLfloat nx, GLfloat ny, GLfloat nz )
{
    GLdouble normal[3], cross[3], zaxis[3];
    GLdouble angle;

#define DegreesToRadians     (M_PI / (GLfloat) 180.0)

    glColor3f( 1.0, 0.0, 0.0 );
    glPushMatrix();
         glTranslatef( px, py, pz );
         glBegin( GL_LINES );
              glVertex3f( 0.0, 0.0, 0.0 );
              glVertex3f( nx, ny, nz );
         glEnd();

         /* normalize the normal vector */
         normal[0] = nx; normal[1] = ny; normal[2] = nz;
         normalize( normal );

         /* determine angle between z axis and normal */
         zaxis[0] = 0; zaxis[1] = 0; zaxis[2] = 1;
         angle = acos( dot3( zaxis, normal ) )/DegreesToRadians;
         
         if ( angle != 0.0 ) {
              /* find the axis of rotation */
              crossprod( zaxis, normal, cross );
              glRotatef( angle, cross[0], cross[1], cross[2] );
         }
         /* move to end of normal vector */
         glTranslatef( 0.0, 0.0, 1.0 );
         glutSolidCone( 0.05, 0.1, 8, 8 );
    glPopMatrix();
}

GLvoid
drawScene( GLvoid )
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();

         polarView( distance, azimAngle, incAngle, twistAngle );

         /* Draw a lit flat-shaded triangle on the left */
         glPushMatrix();
              glTranslatef( -2.0, 0.0, 0.0 );

              /* Turn on lighting computations */
              glEnable( GL_LIGHTING );

              glBegin( GL_TRIANGLES );
                   glNormal3f( 0.0, 0.0, 1.0 );
                   glVertex2f( 0.0, 1.0 );
                   glVertex2f( -0.866, -0.50 );
                   glVertex2f( 0.866, -0.50 );
              glEnd();

              /* Turn off lighting computations */
              glDisable( GL_LIGHTING );
              
              /* Draw an unlit line representing the face normal */
              drawNormal( 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 );
         glPopMatrix();

         /* Draw a lit, smooth-shaded triangle on the right */
         glPushMatrix();
              glTranslatef( 2.0, 0.0, 0.0 );

              /* Turn on lighting computations */
              glEnable( GL_LIGHTING );

              glBegin( GL_TRIANGLES );
                   glNormal3f( 0.0, 0.0, 1.0 );
                   glVertex2f( 0.0, 1.0 );
                   glNormal3f( 0.455, 0.796, 0.398 );
                   glVertex2f( -0.866, -0.50 );
                   glNormal3f( 0.162, 0.566, 0.808 );
                   glVertex2f( 0.866, -0.50 );
              glEnd();

              /* Turn off lighting computations */
              glDisable( GL_LIGHTING );

              /* Draw unlit lines representing the vertex normals */
              drawNormal( 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 );
              drawNormal( -0.866, -0.50, 0.0, 0.455, 0.796, 0.398 );
              drawNormal( 0.866, -0.50, 0.0, 0.162, 0.566, 0.808 );
         glPopMatrix();
    glPopMatrix();

    glutSwapBuffers();
}

Notes:

This program draws two triangles. The triangle on the left is given a single face normal, and therefore, it is rendered as a flat-shaded triangle. Each vertex in the triangle on the right is given a different normal, so the colors between each vertex are interpolated. In other words, the triangle on the right is smooth-shaded.

The drawNormal() function just draws an unlit arrow representing the normal vector. It is useful for showing where the normal is pointed.

Notice that GL_LIGHT0 is enabled in initgfx() but this does not turn on lighting. The program turns lighting on only when it needs it (to draw the triangles in drawScene()) and then turns it off to draw the normals.

Material Properties

An object has five properties that describe its appearance

Notes:

An object has five material properties that describe its appearance.

Specifying Material Properties

GLvoid glMaterialf( GLenum face, GLenum pname, 
                    const GLfloat param )
GLvoid glMaterialfv( GLenum face, GLenum pname,
                     const GLfloat *params )

Notes:

For GL_DIFFUSE, GL_AMBIENT, GL_AMBIENT_AND_DIFFUSE, GL_EMISSION, and GL_SPECULAR you must specify an array of RGBA color values.

GL_AMBIENT_AND_DIFFUSE sets ambient and diffuse colors to the same values with one function call. It is provided for convenience since the ambient and diffuse material properties are often set to the same value.

GL_SHININESS takes a single value ranging from 0 (dull) to 128 (shiny). The default value is 0. The shininess and specular properties work together; shininess controls the specular highlight concentration. Since shininess is set with a single value, you can use glMaterialf() to specify it.

Example: materials.c

/*  materials.c - Use material properties to change the color and
 *             reflection models for some objects
 *
 *  Left Mouse Button  - change incidence and azimuth angles
 *  Middle Mousebutton - change the twist angle based on
 *                       horizontal mouse movement
 *  Right Mousebutton  - zoom in and out based on vertical
 *                       mouse movement
 *  <R> Key            - reset viewpoint
 *  Escape key         - exit the program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <math.h>
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  drawScene( GLvoid );
GLvoid  reshape( GLsizei, GLsizei );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  mouse( GLint, GLint, GLint, GLint );
GLvoid  motion( GLint, GLint );

void resetView( GLvoid );
void polarView( GLfloat, GLfloat, GLfloat, GLfloat );
void printHelp( char * );

/* Global Definitions */

#define KEY_ESC     27      /* ascii value for the escape key */

/* Global Variables */

enum             actions { MOVE_EYE, TWIST_EYE, ZOOM, MOVE_NONE };
static GLint     action;

static GLdouble    xStart = 0.0, yStart = 0.0;

static GLfloat     fovy, nearClip, farClip, distance, twistAngle, incAngle, azimAngle;

void
main( int argc, char *argv[] )
{
    GLsizei width, height;

    glutInit( &argc, argv );

    width = glutGet( GLUT_SCREEN_WIDTH ); 
    height = glutGet( GLUT_SCREEN_HEIGHT );
    glutInitWindowPosition( width / 4, height / 4 );
    glutInitWindowSize( (width / 2) - 4, height / 2 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initgfx();

    glutMouseFunc( mouse );
    glutMotionFunc( motion );
    glutKeyboardFunc( keyboard );
    glutReshapeFunc( reshape );
    glutDisplayFunc( drawScene ); 

    printHelp( argv[0] );

    glutMainLoop();
}

void
printHelp( char *progname )
{
    fprintf(stdout, 
         "\n%s - demonstrate lighting material properties\n\n" 
         "Left Mousebutton    - move eye position\n"
         "Middle Mousebutton     - change twist angle\n"
         "Right Mousebutton      - move up / down to zoom in / out\n"
         "<R> Key          - reset viewpoint\n"
         "Escape Key          - exit the program\n\n",
         progname);
}

GLvoid
initgfx( GLvoid )
{
    glClearColor( 0.0, 0.0, 0.0, 1.0 );

    glEnable( GL_DEPTH_TEST );

    fovy = 60.0;    /* field of view in Y */
    nearClip = 3.0;     /* Near clipping plane location */
    farClip  = 12.0;    /* Far clipping plane location */

    resetView();

    /* Turn on a default light */
    glEnable( GL_LIGHT0 );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
    switch (key) {
    case 'R':
         resetView();
         glutPostRedisplay();
         break;
    case KEY_ESC:   /* Exit whenever the Escape key is pressed */
         exit(0);
    }
}

GLvoid 
mouse( GLint button, GLint state, GLint x, GLint y )
{
    static GLint buttons_down = 0;

    if (state == GLUT_DOWN) {
         switch (button) {
         case GLUT_LEFT_BUTTON:
              action = MOVE_EYE;
              break;
         case GLUT_MIDDLE_BUTTON:
              action = TWIST_EYE;
              break;
         case GLUT_RIGHT_BUTTON:
              action = ZOOM;
              break;
         }

         /* Update the saved mouse position */
         xStart = x;
         yStart = y;
    } else {
         if (--buttons_down == 0)
              action = MOVE_NONE;
    }
}

GLvoid
motion( GLint x, GLint y )
{
    switch (action) {
    case MOVE_EYE:
         /* Adjust the eye position based on the mouse position */
         azimAngle += (GLdouble) (x - xStart);
         incAngle -= (GLdouble) (y - yStart);
         break;
    case TWIST_EYE:
         /* Adjust the eye twist based on the mouse position */
         twistAngle = fmod(twistAngle+(x - xStart), 360.0);
         break;
    case ZOOM:
         /* Adjust the eye distance based on the mouse position */
         distance -= (GLdouble) (y - yStart)/10.0;
         break;
    default:
         printf("unknown action %d\n", action);
    }
    
    /* Update the stored mouse position for later use */
    xStart = x;
    yStart = y;

    glutPostRedisplay();
}

void
resetView( GLvoid )
{
    distance = nearClip + (farClip - nearClip) / 2.0;
    twistAngle = 0.0;    /* rotation of viewing volume (camera) */
    incAngle = 60.0;
    azimAngle = 0.0;
    fovy = 60.0;    /* Field of view in Y angle */
}

GLvoid
reshape( GLsizei width, GLsizei height )
{
    GLdouble     aspect;

    glViewport( 0, 0, width, height );

    aspect = (GLdouble) width / (GLdouble) height;

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( fovy, aspect, nearClip, farClip );
    glMatrixMode( GL_MODELVIEW );
}

void
polarView( GLfloat distance, GLfloat azimuth, GLfloat incidence,
              GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

GLvoid
drawScene( GLvoid )
{
    /* Define a few materials properties */
    GLfloat   redAmbient[] = { 0.3, 0.1, 0.1, 1.0 };
    GLfloat   redDiffuse[] = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat   blueAmbient[] = { 0.1, 0.1, 0.3, 1.0 };
    GLfloat   blueDiffuse[] = { 0.0, 0.0, 1.0, 1.0 };
    GLfloat   yellowDiffuse[] = { 1.0, 1.0, 0.0, 1.0 };
    GLfloat   yellowEmission[] = { 0.6, 0.6, 0.0, 1.0 };
    GLfloat   defaultEmission[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat   whiteSpecular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat   greenSpecular[] = { 0.0, 1.0, 0.0, 1.0 };
    GLfloat   defaultSpecular[] = { 0.0, 0.0, 0.0, 1.0 };

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();
         polarView( distance, azimAngle, incAngle, twistAngle );

         XYZaxes();

         glEnable( GL_LIGHTING );

         glMaterialfv( GL_FRONT, GL_EMISSION, defaultEmission );

         /* Set properties for a very shiny red material,
          * with a green highlight */
         glMaterialfv( GL_FRONT, GL_AMBIENT, redAmbient );
         glMaterialfv( GL_FRONT, GL_DIFFUSE, redDiffuse );
         glMaterialfv( GL_FRONT, GL_SPECULAR, greenSpecular );
         glMaterialf( GL_FRONT, GL_SHININESS,
                        128.0 ); /* tight distribution */
         glPushMatrix();
              glTranslatef( -2.0, 1.5, 0.0 );
              glutSolidSphere( 0.7, 31, 31 );
         glPopMatrix();

         /* Set properties for a dull blue material with
          *   a small white highlight */
         glMaterialfv( GL_FRONT, GL_AMBIENT, blueAmbient );
         glMaterialfv( GL_FRONT, GL_DIFFUSE, blueDiffuse );
         glMaterialfv( GL_FRONT, GL_SPECULAR, whiteSpecular );
         glMaterialf( GL_FRONT, GL_SHININESS,
                        20.0 ); /* not very shiny */
         glPushMatrix();
              glTranslatef( 2.5, 0.0, 0.0 );
              glutSolidTorus( 0.25, 0.75, 16, 31 );
         glPopMatrix();

         /* Set properties for a dull yellow glowing material.
          * Shininess property will be the same as the previous
          * material since it is not set differently. */
         glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellowDiffuse);
         glMaterialfv( GL_FRONT, GL_EMISSION, yellowEmission );
         glMaterialfv( GL_FRONT, GL_SPECULAR, defaultSpecular );

         glPushMatrix();
              glTranslatef( 0.0, 2.0, 2.0 );
              glutSolidCube( 0.5 );
         glPopMatrix();

         glDisable( GL_LIGHTING );

    glPopMatrix();

    glutSwapBuffers();
}

Notes:

This program draws three objects: a shiny red sphere with a green highlight, a dull blue torus with a white highlight, and a glowing yellow cube.

Notice the material declarations at the beginning of drawScene(). These are the material properties used for the objects in the scene.

Note: If you do not set a material property for an object, it uses either the default value, or the value set with the previous call to glMaterial*() for that property.

Specifying Fast Material Changes

glEnable(GL_COLOR_MATERIAL)

GLvoid glColorMaterial( GLenum face, GLenum property )

Notes:

Legal values for face are GL_FRONT, GL_BACK, and GL_FRONT_AND_BACK (default).

Legal values for property are GL_DIFFUSE, GL_AMBIENT, GL_SPECULAR, GL_EMISSION, and GL_AMBIENT_AND_DIFFUSE (default).

Fast Material Changes Example

GLfloat redDiffuse = { 1.0, 0.0, 0.0 };
GLfloat blueDiffuse = { 0.0, 0.0, 1.0 };
       
/* set the property and face to be 
 * affected by glColor*() calls */
glColorMaterial( GL_FRONT, GL_DIFFUSE );
    
/* enable fast material changes */
glEnable( GL_COLOR_MATERIAL );
    
/* change the diffuse material property to red */
glColor3fv( redDiffuse );
drawRedMaterialObjects();
    
/* change the diffuse material property to blue */
glColor3fv( blueDiffuse );
drawBlueMaterialObjects();
    
/* don't forget to disable fast material changes */
glDisable( GL_COLOR_MATERIAL );

Notes:

Note that changes can still be made to other materials using glMaterial*().

Be sure to turn off GL_COLOR_MATERIAL when not needed; otherwise, all of your glColor*() calls get interpreted as material changes, even if lighting is disabled.

Example: colorMaterial.c

/* colorMaterial.c - demonstrates using glColorMaterial to quickly 
 *             change material properties.
 *
 *     Escape key                      - exit the program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <math.h>
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  drawScene( GLvoid );
GLvoid  reshape( GLsizei, GLsizei );
GLvoid  keyboard( GLubyte, GLint, GLint );

void resetView( GLvoid );
void polarView( GLfloat, GLfloat, GLfloat, GLfloat );
void printHelp( char * );

/* Global Definitions */

#define KEY_ESC     27      /* ascii value for the escape key */

/* Global Variables */

static GLdouble     fovy, nearClip, farClip; 
static GLfloat      distance, twistAngle, incAngle, azimAngle;

GLvoid
main( int argc, char *argv[] )
{
    GLsizei width, height;

    glutInit( &argc, argv );

    width = glutGet( GLUT_SCREEN_WIDTH ); 
    height = glutGet( GLUT_SCREEN_HEIGHT );
    glutInitWindowPosition( width / 4, height / 4 );
    glutInitWindowSize( (width / 2) - 4, height / 2 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initgfx();

    glutKeyboardFunc( keyboard );
    glutReshapeFunc( reshape );
    glutDisplayFunc( drawScene ); 

    printHelp( argv[0] );

    glutMainLoop();
}

void
printHelp( char *progname )
{
    fprintf(stdout, "\n%s - demonstrate fast material changes\n\n"
         "Escape key   - exit the program\n\n", progname);
}

GLvoid
initgfx( GLvoid )
{
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 10.0 };

    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    /* changing AMBIENT and DIFFUSE properties to the same value
     * for the front face only
     */
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glEnable( GL_DEPTH_TEST );

    fovy = 60.0;    /* field of view in Y */
    nearClip = 3.0;     /* Near clipping plane location */
    farClip  = 12.0;    /* Far clipping plane location */

    resetView();
}

GLvoid
keyboard( GLubyte key, GLint x, GLint y )
{
    switch (key) {
    case KEY_ESC:
         exit(0);
    }
}

void
resetView( GLvoid )
{
    distance = nearClip + (farClip - nearClip) / 2.0;
    twistAngle = 0.0;    /* rotation of viewing volume (camera) */
    incAngle = 0.0;
    azimAngle = 0.0;
}

GLvoid
reshape( GLsizei width, GLsizei height )
{
    GLdouble     aspect;

    glViewport( 0, 0, width, height );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    aspect = (GLdouble) width / (GLdouble) height;
    gluPerspective( fovy, aspect, nearClip, farClip );
    glMatrixMode( GL_MODELVIEW );
}

void
polarView( GLfloat distance, GLfloat azimuth, GLfloat incidence,
              GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

GLvoid
drawScene( GLvoid )
{
    int i,  slices = 8;
    
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glPushMatrix(); 
    
         polarView( distance, azimAngle, incAngle, 0 );

         for ( i = 0; i < slices; i++ )
         {
              /* change ambient and diffuse material properties
               * using GL_COLOR_MATERIAL mode
               */
              glColor3f( i/10.0, i/10.0, 1.0 - i/10.0 ); 
              glPushMatrix(); 
                   glRotatef( i * 360.0/slices, 0, 0, 1 );
                   glTranslatef( 1.5, 0.0, 0.0 );
                   glRotatef( i * 360.0/slices, 0, 1, 0 );
                   glutSolidTorus( 0.25, 0.75, 8, 15 );
              glPopMatrix();
         }
    glPopMatrix();
    glutSwapBuffers();
}

Notes:

Note the call to glColorMaterial() in initgfx() to indicate that both the ambient and diffuse properties should be changed for the front faces whenever you call glColor*().

Also note that you need to enable GL_COLOR_MATERIAL to make this take effect.

Because GL_COLOR_MATERIAL is enabled for the ambient and diffuse properties, the calls to glColor3f() in drawScene() change the ambient and diffuse property of each torus in the ring.

Lab: Material Properties

This lab has two parts: tutorial, and programming.

Material Tutorial

  1. Change directory to siggraph_2001_demos/bin.

  2. Run ./lightmaterial, the lightmaterial tutorial program.

    • To reset the parameters at any time, right-click in the Command manipulation window and select Reset parameter(s).
    • To select a different model, right-click in the Screen-space view.

  3. Experiment with the lower half of the Command manipulation window, where you can modify the material properties passed to glMaterial:

    • Change the ambient, diffuse, emission and specular colors, by dragging on the RGB values in the material_K arrays. Note the differences in the Screen-space view.

    • Modify the shininess value. Do you see a difference in the Screen-space view? If not, it may be that the specular color is too dark or the emissive color is too bright.

Setting Material Properties

  1. Change directory to ../tutorial/exercises.

  2. Modify your solar9.c program to do the following:

    • Enable lighting.

    • Change the material properties for the sun, Earth, and moon. Use fast material changes for one of the material properties.

    • Make the sun "glow".

  3. * Change directory to .

  4. * Copy your viewing.c program to material.c and modify it to enable lighting for at least one of the objects in your scene.

    • Try specifying the normals for a simple object (line, polygon, and so on) in your scene. Use either face or vertex normals.

    • Set material properties for one or more objects in your scene.

    • Put a large, multi-sided polygon in your scene. Use fast material changes to set a different material for each vertex in your polygon.

Specifying Light Source Properties

GLvoid glLightfv( GLenum light, GLenum pname, 
                    const GLfloat *params )

Notes:

The value <n> can be from 0 to the maximum number of supported lights minus one. An OpenGL implementation is required to support at least eight lights, but can have more. To find the maximum number of lights an implementation supports use:

       glGetIntegerv( GL_MAX_LIGHTS, &maxLights );

Vector and scalar forms:

Scalar Forms Vector Forms
glLightf( light, pname, val ) glLightfv( light, pname, *v )
glLighti( light, pname, val ) glLightiv( light, pname, *v )

Scalar forms can only be used for setting the spotlight exponent and cutoff, and the attenuation constants.

This module covers only how to set light position and intensities.

Querying Light Source Properties

GLvoid glGetLightfv(GLenum light, GLenum pname,
                    GLfloat *params)

Notes:

The values returned by GL_POSITION and GL_SPOT_DIRECTION are in eye coordinates.

Specifying Light Positions

GLvoid glLightfv( GLenum light, GL_POSITION,
                  const GLfloat *params )

Notes:

For an infinitely distant light, the direction of the light is assumed to be from the point (x, y, z) toward the origin. Never set the infinite light direction to (0,0,0,0), as it will be undefined. The rays from the light are assumed to be parallel. The sun is an example of a real-world infinite light. By the time the light gets to Earth, its rays are effectively parallel.

An example of a local light would be a light bulb. By default, the light radiates in all directions from the position specified by (x, y, z). (You will see how to make the light only radiate in one direction later in this module.)

Local light sources are much more compute intensive than infinite light sources.

Light Position and the Modelview Matrix

Notes:

The colors across the face of a smooth-shaded polygon are determined by the colors calculated for the vertices. Because of this, you probably want to avoid using large polygons with local lights.

Note: If you locate the light near the middle of the polygon, the vertices might be too far away to receive much light making the whole polygon look darker than you intended. To avoid this problem, break up large polygons into smaller ones.

Light Source That Moves With the Eye

To create a light source that moves with your viewpoint, bind the light before your viewing transformation

To fix the light source at the same position as the eye, set the light position to (0,0,1,0) for an infinite light or (0,0,0,1) for a local light

Notes:

Since the light is often coming from the same direction as the eye, it can be thought of as a head light. The materials.c program is an example of this.

Light Source Fixed in a Scene

To create a light source that stays at a fixed position in your scene, bind the light after your viewing transformation

Notes:

By binding the light after the viewing transformation, the position of the light is not affected by where the eye is. It stays at the same place in the scene.

Example: lightPosition.c

/* lightPosition.c - demonstrates how the light position is affected by 
 *             the modelview matrix
 *
 *  Left Mouse Button       - change incidence and azimuth angles
 *  Middle Mousebutton      - change the twist angle based on
 *                            horizontal mouse movement
 *  Right Mousebutton       - zoom in and out based on vertical
 *                            mouse movement
 *  <l> key                 - toggle light binding
 *  Escape key              - exit the program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <math.h>
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  drawScene( GLvoid );
GLvoid  reshape( GLsizei, GLsizei );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  mouse( GLint, GLint, GLint, GLint );
GLvoid  motion( GLint, GLint );

void resetView( GLvoid );
void polarView( GLfloat, GLfloat, GLfloat, GLfloat );
void printHelp( char * );

/* Global Definitions */

#define KEY_ESC     27      /* ascii value for the escape key */

/* Global Variables */

enum lighttypes { EYE_LIGHT, SCENE_LIGHT };
static GLint        lightpos;

enum actions { MOVE_EYE, TWIST_EYE, ZOOM, MOVE_NONE };
static GLint        action;

static GLdouble     xStart = 0.0, yStart = 0.0;

static GLfloat      fovy, nearClip, farClip; 
static GLfloat      distance, twistAngle, incAngle, azimAngle;

void
main( int argc, char *argv[] )
{
    GLsizei width, height;

    glutInit( &argc, argv );

    width = glutGet( GLUT_SCREEN_WIDTH ); 
    height = glutGet( GLUT_SCREEN_HEIGHT );
    glutInitWindowPosition( width / 4, height / 4 );
    glutInitWindowSize( (width / 2) - 4, height / 2 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initgfx();

    glutMouseFunc( mouse );
    glutMotionFunc( motion );
    glutKeyboardFunc( keyboard );
    glutReshapeFunc( reshape );
    glutDisplayFunc( drawScene ); 

    printHelp( argv[0] );

    glutMainLoop();
}

void
printHelp( char *progname )
{
    fprintf(stdout, "\n%s - demonstrate how the modelview matrix "
         "affects the light position\n\n" 
         "Left Mousebutton    - move eye position\n"
         "Middle Mousebutton     - change twist angle\n"
         "Right Mousebutton      - move up / down to zoom in / out\n"
         "<l> Key          - toggle light binding\n"
         "Escape Key          - exit the program\n\n",
         progname);

    if ( lightpos == EYE_LIGHT ) 
         printf("Light position attached to Viewpoint\n");
    else if ( lightpos == SCENE_LIGHT )
         printf("Light position fixed in scene\n");
}

GLvoid
initgfx( GLvoid )
{
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glEnable( GL_DEPTH_TEST );

    fovy = 60.0;    /* field of view in Y */
    nearClip = 3.0;     /* Near clipping plane location */
    farClip  = 12.0;    /* Far clipping plane location */

    resetView();

    lightpos = EYE_LIGHT;

    /* Turn on a default light */
    glEnable( GL_LIGHT0 );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
    switch (key) {
    case 'l':    /* toggle light position */
         if ( lightpos == EYE_LIGHT ) {
              lightpos = SCENE_LIGHT;
              printf("Light position fixed in scene\n");
         } else if ( lightpos == SCENE_LIGHT ) {
              lightpos = EYE_LIGHT;
              printf("Light position attached to viewpoint\n");
         }
         glutPostRedisplay();
         break;
    case KEY_ESC:   /* Exit when the Escape key is pressed */
         exit(0);
    }
}

GLvoid 
mouse( GLint button, GLint state, GLint x, GLint y )
{
    static GLint buttons_down = 0;

    if (state == GLUT_DOWN) {
         switch (button) {
         case GLUT_LEFT_BUTTON:
              action = MOVE_EYE;
              break;
         case GLUT_MIDDLE_BUTTON:
              action = TWIST_EYE;
              break;
         case GLUT_RIGHT_BUTTON:
              action = ZOOM;
              break;
         }

         /* Update the saved mouse position */
         xStart = x;
         yStart = y;
    } else {
         if (--buttons_down == 0)
              action = MOVE_NONE;
    }
}

GLvoid
motion( GLint x, GLint y )
{
    switch (action) {
    case MOVE_EYE:
         /* Adjust the eye position based on the mouse position */
         azimAngle += (GLdouble) (x - xStart);
         incAngle -= (GLdouble) (y - yStart);
         break;
    case TWIST_EYE:
         /* Adjust the eye twist based on the mouse position */
         twistAngle = fmod(twistAngle+(x - xStart), 360.0);
         break;
    case ZOOM:
         /* Adjust the eye distance based on the mouse position */
         distance -= (GLdouble) (y - yStart)/10.0;
         break;
    default:
         printf("unknown action %d\n", action);
    }
    
    /* Update the stored mouse position for later use */
    xStart = x;
    yStart = y;

    glutPostRedisplay();
}

void
resetView( GLvoid )
{
    distance = nearClip + (farClip - nearClip) / 2.0;
    twistAngle = 0.0;    /* rotation of viewing volume (camera) */
    incAngle = 60.0;
    azimAngle = 0.0;
    fovy = 60.0;    /* Field of view in Y angle */
}

GLvoid
reshape( GLsizei width, GLsizei height )
{
    GLdouble     aspect;

    glViewport( 0, 0, width, height );

    aspect = (GLdouble) width / (GLdouble) height;

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( fovy, aspect, nearClip, farClip );
    glMatrixMode( GL_MODELVIEW );
}

void
polarView( GLfloat distance, GLfloat azimuth, GLfloat incidence,
              GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

GLvoid
drawScene( GLvoid )
{
    /* Define a few materials properties */
    GLfloat   redAmbient[] = { 0.3, 0.1, 0.1, 1.0 };
    GLfloat   redDiffuse[] = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat   blueAmbient[] = { 0.1, 0.1, 0.3, 1.0 };
    GLfloat   blueDiffuse[] = { 0.0, 0.0, 1.0, 1.0 };
    GLfloat   yellowDiffuse[] = { 1.0, 1.0, 0.0, 1.0 };
    GLfloat   yellowEmission[] = { 0.6, 0.6, 0.0, 1.0 };
    GLfloat   defaultEmission[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat   whiteSpecular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat   greenSpecular[] = { 0.0, 1.0, 0.0, 1.0 };
    GLfloat   defaultSpecular[] = { 0.0, 0.0, 0.0, 1.0 };

    /* infinite light */
    GLfloat   lightPosition[] = { 0.0, 0.0, 1.0, 0.0 };

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();

       if ( lightpos == EYE_LIGHT ) {
           /* By setting the light position before the viewing
            * transformation, the light moves with the eye.
            * (In other words, it is always in the same 
            * position relative to the viewpoint.)
            */
           glLightfv( GL_LIGHT0, GL_POSITION, lightPosition);
        }

        polarView( distance, azimAngle, incAngle, twistAngle );

        XYZaxes();

        if ( lightpos == SCENE_LIGHT ) {
           /* By setting the light positions after the 
            * viewing transformation, the light(s) will 
            * be fixed in the scene.
            */
           glLightfv( GL_LIGHT0, GL_POSITION, lightPosition );
        } 

        glEnable( GL_LIGHTING );

        glMaterialfv( GL_FRONT, GL_EMISSION, defaultEmission );

        /* Set properties for a shiny red material,
       * with a green highlight */
        glMaterialfv( GL_FRONT, GL_AMBIENT, redAmbient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, redDiffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, greenSpecular );
        glMaterialf( GL_FRONT, GL_SHININESS, 128.0 );
        glPushMatrix();
           glTranslatef( -2.0, 1.5, 0.0 );
           glutSolidSphere( 0.7, 31, 31 );
        glPopMatrix();

        /* Set properties for a dull blue material with
       *   a small white highlight */
        glMaterialfv( GL_FRONT, GL_AMBIENT, blueAmbient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, blueDiffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, whiteSpecular );
        glMaterialf( GL_FRONT, GL_SHININESS, 20.0 );
        glPushMatrix();
           glTranslatef( 2.5, 0.0, 0.0 );
           glutSolidTorus( 0.25, 0.75, 16, 31 );
        glPopMatrix();

        /* Set properties for a yellow glowing material */
        glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellowDiffuse);
        glMaterialfv( GL_FRONT, GL_EMISSION, yellowEmission );
        glMaterialfv( GL_FRONT, GL_SPECULAR, defaultSpecular );

        glPushMatrix();
           glTranslatef( 0.0, 2.0, 2.0 );
           glutSolidCube( 0.5 );
        glPopMatrix();

        glDisable( GL_LIGHTING );

    glPopMatrix();
    glutSwapBuffers();
}

Notes:

This program demonstrates how the modelview matrix affects the light position. Pressing the <l> key toggles between a light fixed in the scene and a light that moves with the viewpoint.

The program enables the default light (a white light with a white specular component) in initgfx(). In drawScene(), the light is specified as an infinite light shining from the positive z direction (0, 0, 1) toward the origin.

The program begins with the light attached to the eye. It binds the light before the viewing transformation. The light is always coming from the position of the eye, towards the scene, no matter where the viewpoint is located. Notice how the sides of the objects facing the screen are always lit.

When the <l> key is pressed, the program toggles the light position so that the light is fixed in the scene. It does this by binding the light after the viewing transformations, but before the modeling transformations. This forces the light to always be positioned at the same location within the scene. Note that as the viewpoint moves, the same side of the sphere is always lit.

Moving Light Source

To create a light source that moves in your scene, bind the light after your viewing transformation and give the light source its own modeling transformations

/* viewing transformation */
polarView( ... );
glPushMatrix();
    /* light transformations */
    glRotatef( ... );
    glTranslatef( ... );

    /* bind light */
    glLightfv( GL_LIGHT0, GL_POSITION, lightPosition );

    /* draw a small (unlit) white sphere to
     * represent the light in our scene. */
    glColor3f( 1.0, 1.0, 1.0 );
    glutSolidSphere( 0.07, 4, 7 );
glPopMatrix();
glEnable( GL_LIGHTING );

Notes:

If desired, you can create an object to represent the light source.

Note: This code segment does not enable lighting until after drawing the object that represents the light. Therefore, the object representing the light is not lit.

The call to glEnable() does not affect the positioning of the light source itself. The position of the light source is only affected by the ModelView matrix. It does not depend on whether lighting is actually enabled when glLightfv() is called.

Specifying the Lighting Model

GLvoid glLightModelfv(GLenum pname, const GLfloat *params)

pname Default Value Description
GL_LIGHT_MODEL_AMBIENT (0.2, 0.2, 0.2, 1.0) Specifies global ambient illumination; not from any particular light source
GL_LIGHT_MODEL_LOCAL_VIEWER 0.0 (infinite) Specifies whether viewer is local to the scene or an infinite distance away
GL_LIGHT_MODEL_TWO_SIDE 0.0 (one-sided) Specifies whether to perform lighting calculations for both the front and back surfaces

Notes:

GL_LIGHT_MODEL_AMBIENT specifies the ambient light color when no lights are on. It represents light that is not from any particular source. The default value {0.2, 0.2, 0.2, 1.0} yields a small amount of white ambient light. So, even if you do not add a specific light source to your scene, you can still see the objects in the scene.

GL_LIGHT_MODEL_LOCAL_VIEWER changes the assumptions made about the location of the viewpoint. (It does not actually move the viewpoint).

Example: movingLight.c

/* movingLight.c - Set up a light that moves independent 
 *                 of the objects in the scene.
 *
 *  Left Mouse Button       - change incidence and azimuth angles
 *  Middle Mousebutton      - change the twist angle based on
 *                               horizontal mouse movement
 *  Right Mousebutton       - zoom in and out based on vertical
 *                               mouse movement
 *  <a> key                 - toggle light animation
 *  <m> key                 - toggle local/infinite viewer
 *  Escape key              - exit the program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <math.h>
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  animate( GLvoid );
GLvoid  visibility( GLint );
GLvoid  drawScene( GLvoid );
GLvoid  reshape( GLsizei, GLsizei );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  mouse( GLint, GLint, GLint, GLint );
GLvoid  motion( GLint, GLint );

void resetView( GLvoid );
void polarView( GLfloat, GLfloat, GLfloat, GLfloat );
void printHelp( char * );

/* Global Definitions */

#define KEY_ESC        27      /* ascii value for the escape key */

/* Global Variables */

static GLfloat     lightAngle = 0.0;               /* controls light rotation */

static GLboolean   animateLight = GL_TRUE;
static GLboolean   localLight = GL_FALSE;

enum               actions { MOVE_EYE, TWIST_EYE, ZOOM, MOVE_NONE };
static GLint       action;

static GLdouble    xStart = 0.0, yStart = 0.0;

static GLfloat     fovy, nearClip, farClip, distance, twistAngle, incAngle, azimAngle;

void
main( int argc, char *argv[] )
{
    GLsizei width, height;

    glutInit( &argc, argv );

    width = glutGet( GLUT_SCREEN_WIDTH ); 
    height = glutGet( GLUT_SCREEN_HEIGHT );
    glutInitWindowPosition( width / 4, height / 4 );
    glutInitWindowSize( (width / 2) - 4, height / 2 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initgfx();

    glutIdleFunc( animate );
    glutVisibilityFunc( visibility );
    glutMouseFunc( mouse );
    glutMotionFunc( motion );
    glutKeyboardFunc( keyboard );
    glutReshapeFunc( reshape );
    glutDisplayFunc( drawScene ); 

    printHelp( argv[0] );

    glutMainLoop();
}

void
printHelp( char *progname )
{
    fprintf(stdout, "\n%s - demonstrate how to add a moving light\n\n" 
         "Left Mousebutton      - move eye position\n"
         "Middle Mousebutton    - change twist angle\n"
         "Right Mousebutton     - move up / down to zoom in / out\n"
         "<a> Key               - toggle light animation\n"
         "<m> Key               - toggle local/infinite viewer\n"
         "Escape Key            - exit the program\n\n",
         progname);
}

GLvoid
initgfx( GLvoid )
{
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glEnable( GL_DEPTH_TEST );

    nearClip = 2.0;     /* Near clipping plane location */
    farClip  = 15.0;    /* Far clipping plane location */

    resetView();

    /* Turn on a default light */
    glEnable( GL_LIGHT0 );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
    GLfloat infinite[] = {0.0};
    GLfloat local[] = {1.0};

    switch (key) {
    case 'a':    /* toggle light animation */
         animateLight = !animateLight;
         if ( animateLight )
            glutIdleFunc(animate);
         else 
            glutIdleFunc(NULL);
         glutPostRedisplay();
         break;
    case 'm':   /* toggle lighting model */
        localLight = !localLight;
        if ( localLight )
        {
            glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local);
            printf("local viewer ON\n");
        }
        else
        {
            glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, infinite);
            printf("local viewer OFF\n");
        }
        glutPostRedisplay();
        break;
    case KEY_ESC:   /* Exit when the Escape key is pressed */
         exit(0);
    }
}

GLvoid 
mouse( GLint button, GLint state, GLint x, GLint y )
{
    static GLint buttons_down = 0;

    if (state == GLUT_DOWN) {
         switch (button) {
         case GLUT_LEFT_BUTTON:
              action = MOVE_EYE;
              break;
         case GLUT_MIDDLE_BUTTON:
              action = TWIST_EYE;
              break;
         case GLUT_RIGHT_BUTTON:
              action = ZOOM;
              break;
         }

         /* Update the saved mouse position */
         xStart = x;
         yStart = y;
    } else {
         if (--buttons_down == 0)
              action = MOVE_NONE;
    }
}

GLvoid
motion( GLint x, GLint y )
{
    switch (action) {
    case MOVE_EYE:
         /* Adjust the eye position based on the mouse position */
         azimAngle += (GLdouble) (x - xStart);
         incAngle -= (GLdouble) (y - yStart);
         break;
    case TWIST_EYE:
         /* Adjust the eye twist based on the mouse position */
         twistAngle = fmod(twistAngle+(x - xStart), 360.0);
         break;
    case ZOOM:
         /* Adjust the eye distance based on the mouse position */
         distance -= (GLdouble) (y - yStart)/10.0;
         break;
    default:
         printf("unknown action %d\n", action);
    }
    
    /* Update the stored mouse position for later use */
    xStart = x;
    yStart = y;

    glutPostRedisplay();
}

void
resetView( GLvoid )
{
    distance = nearClip + (farClip - nearClip) / 2.0;
    twistAngle = 0.0;    /* rotation of viewing volume (camera) */
    incAngle = 60.0;
    azimAngle = 0.0;
    fovy = 60.0;    /* Field of view in Y angle */
}

GLvoid
reshape( GLsizei width, GLsizei height )
{
    GLdouble     aspect;

    glViewport( 0, 0, width, height );

    aspect = (GLdouble) width / (GLdouble) height;

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( fovy, aspect, nearClip, farClip );
    glMatrixMode( GL_MODELVIEW );
}

GLvoid 
animate( GLvoid )
{
    /* update the rotation of the light for each scene */
    lightAngle = fmod( (lightAngle + 2.0), 360.0 );

    /* Tell GLUT to redraw the scene */
    glutPostRedisplay();
}

GLvoid
visibility( int state ) 
{
    if (state == GLUT_VISIBLE && animateLight) {
         glutIdleFunc( animate );
    } else {
         glutIdleFunc( NULL );
    }
}

void
polarView( GLfloat distance, GLfloat azimuth, GLfloat incidence,
              GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

GLvoid
drawScene( GLvoid )
{
    /* Define a few materials properties */
    GLfloat   redAmbient[] = { 0.3, 0.1, 0.1, 1.0 };
    GLfloat   redDiffuse[] = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat   blueAmbient[] = { 0.1, 0.1, 0.3, 1.0 };
    GLfloat   blueDiffuse[] = { 0.0, 0.0, 1.0, 1.0 };
    GLfloat   defaultEmission[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat   whiteSpecular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat   greenSpecular[] = { 0.0, 1.0, 0.0, 1.0 };
    GLfloat   defaultSpecular[] = { 0.0, 0.0, 0.0, 1.0 };

    /* local light */
    GLfloat   lightPosition[] = { 0.0, 0.0, 0.0, 1.0 };

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();

        polarView( distance, azimAngle, incAngle, twistAngle );

        XYZaxes();

        /* Animate the light with its own set of transformations */
        glPushMatrix();
           glRotatef( lightAngle, 0.0, 1.0, 1.0 );
           glTranslatef( 2.7, 0.0, 0.0 );

           /* By giving the light position its own modeling
            * transformations (due to the glPushMatrix() 
            * and glPopMatrix() calls), the light moves
            * independently of the objects in the scene.  
            */
           glLightfv( GL_LIGHT0, GL_POSITION, lightPosition );

           /* draw a small (unlit) white sphere to
            * represent the light in our scene. 
            */
           glColor3f( 1.0, 1.0, 1.0 );
           glutSolidSphere( 0.07, 4, 7);
        glPopMatrix();

        glEnable( GL_LIGHTING );

        glMaterialfv( GL_FRONT, GL_EMISSION, defaultEmission );

        /* Set properties for a shiny red material,
         * with a green highlight */
        glMaterialfv( GL_FRONT, GL_AMBIENT, redAmbient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, redDiffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, greenSpecular );
        glMaterialf( GL_FRONT, GL_SHININESS, 128.0 );
        glPushMatrix();
            glTranslatef( -2.5, 0.5, 0.0 );
            glutSolidSphere( 0.7, 31, 31 );
        glPopMatrix();

        glPushMatrix();
            glTranslatef( 1.0, 2.0, 2.0 );
            glutSolidSphere( 0.9, 31, 31 );
        glPopMatrix();

        /* Set properties for a dull blue material with
         *   a small white highlight */
        glMaterialfv( GL_FRONT, GL_AMBIENT, blueAmbient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, blueDiffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, whiteSpecular );
        glMaterialf( GL_FRONT, GL_SHININESS, 20.0 );
        glPushMatrix();
            glTranslatef( 2.5, 0.0, 0.0 );
            glutSolidTorus( 0.25, 0.75, 16, 31 );
        glPopMatrix();

        glDisable( GL_LIGHTING );

    glPopMatrix();

    glutSwapBuffers();
}

Notes:

This program demonstrates how the position of the light can be made to move in a scene. By giving the light source its own transformations, it acts like any other object in the scene. The program uses a small, (unlit) white sphere to represent the light, but the light source would still behave the same way even if the program did not create an object to represent it.

Run movingLight and try to stop the light inside the torus. Then move around so the torus is sideways (so you cannot see the sphere). What is odd about this scene? The light still shines on the sphere even though it is blocked by the torus.

OpenGL lighting does not do any checking for objects that obscure one another. You would have to do your own ray tracing for this.

Ambient Intensity

Light sources do not contribute to the ambient lighting in a scene unless you so specify.

GLvoid glLightfv( GLenum light, GL_AMBIENT, 
                  const GLfloat *params )

Notes:

The GL_AMBIENT parameter refers to the RGBA intensity of the ambient light that a particular light source adds to the scene.

By default, light sources do not contribute to the ambient lighting in a scene. That is, the default ambient intensity is (0.0, 0.0, 0.0, 1.0) for all lights.

Diffuse Intensity

GLvoid glLightfv( GLenum light, GL_DIFFUSE, 
                  const GLfloat *params )

Notes:

The color components specified for lights mean something different than for materials. For a light, the numbers correspond to a percentage of full intensity for each color. For example, if the R, G, and B values for a light's color are all 1.0, the light is the brightest possible white. If the values are 0.5, the color is still white but only at half intensity so it appears gray. If R=G=1 and B=0 (full red and green with no blue), the light appears yellow.

The GL_DIFFUSE parameter probably most closely correlates with what you naturally think of as "the color of a light." It defines the RGBA color of the diffuse light that a particular light source adds to a scene.

By default, GL_DIFFUSE is (1.0, 1.0, 1.0, 1.0) for GL_LIGHT0, which produces a bright white light.

The default value for any other light (GL_LIGHT1, ... , GL_LIGHTn) is (0.0, 0.0, 0.0, 0.0), which produces no light.

Specular Intensity

GLvoid glLightfv( GLenum light, GL_SPECULAR, 
                  const GLfloat *params ) 

Notes:

The GL_SPECULAR parameter affects the color of the specular highlight on an object. Typically, a real-world object (such as a glass bottle) has a specular highlight that is the color of the light shining on it, which is often white.

Therefore, to create a realistic effect, set the GL_SPECULAR parameter to the same value as the GL_DIFFUSE parameter.

By default, GL_SPECULAR is (1.0, 1.0, 1.0, 1.0) for GL_LIGHT0, which produces a bright, white specular component.

The default value for any other light (GL_LIGHT1, ... , GL_LIGHTn) is (0.0, 0.0, 0.0, 0.0), which produces no specular component.

The Lighting Equation

The color at each vertex is the sum of

Notes:

See the module summary for a more detailed description of each of the terms.

Example: lightIntensity.c

/* lightIntensity.c - Use light properties to change the intensity
 *      of the light in our scene.
 *
 *  Left Mouse Button  - change incidence and azimuth angles
 *  Middle Mousebutton - change the twist angle based on
 *                       horizontal mouse movement
 *  Right Mousebutton  - zoom in and out based on vertical
 *                       mouse movement
 *  <a> Key            - toggle ambient light intensity on/off
 *  <d> Key            - toggle diffuse light intensity on/off
 *  <s> Key            - toggle specular light intensity on/off
 *  <R> Key            - reset viewpoint
 *  Escape key         - exit the program
 */

#include <GL/glut.h>    /* includes gl.h, glu.h */

#include <math.h>
#include <stdio.h>

/*  Function Prototypes  */

GLvoid  initgfx( GLvoid );
GLvoid  drawScene( GLvoid );
GLvoid  reshape( GLsizei, GLsizei );
GLvoid  keyboard( GLubyte, GLint, GLint );
GLvoid  mouse( GLint, GLint, GLint, GLint );
GLvoid  motion( GLint, GLint );

GLvoid toggleAmbient( GLvoid );
GLvoid toggleDiffuse( GLvoid );
GLvoid toggleSpecular( GLvoid );

void resetView( GLvoid );
void polarView( GLfloat, GLfloat, GLfloat, GLfloat );
void printHelp( char * );

/* Global Definitions */

#define KEY_ESC     27      /* ascii value for the escape key */

/* Global Variables */

enum               actions { MOVE_EYE, TWIST_EYE, ZOOM, MOVE_NONE };
static GLint       action;

static GLdouble    xStart = 0.0, yStart = 0.0;

static GLfloat     fovy, nearClip, farClip; 
static GLfloat     distance, twistAngle, incAngle, azimAngle;

static GLfloat     angle = 0.0;

void
main( int argc, char *argv[] )
{
    GLsizei width, height;

    glutInit( &argc, argv );

    width = glutGet( GLUT_SCREEN_WIDTH ); 
    height = glutGet( GLUT_SCREEN_HEIGHT );
    glutInitWindowPosition( width / 4, height / 4 );
    glutInitWindowSize( (width / 2) - 4, height / 2 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initgfx();

    glutMouseFunc( mouse );
    glutMotionFunc( motion );
    glutKeyboardFunc( keyboard );
    glutReshapeFunc( reshape );
    glutDisplayFunc( drawScene ); 

    printHelp( argv[0] );

    glutMainLoop();
}

void
printHelp( char *progname )
{
    fprintf(stdout, "\n%s - demonstrate light intensity properties\n\n" 
         "Left Mousebutton      - move eye position\n"
         "Middle Mousebutton    - change twist angle\n"
         "Right Mousebutton     - move up / down to zoom in / out\n"
         "<a> Key          - toggle ambient light intensity on/off\n"
         "<d> Key          - toggle diffuse light intensity on/off\n"
         "<s> Key          - toggle specular light intensity on/off\n"
         "<R> Key          - reset viewpoint\n"
         "Escape Key       - exit the program\n\n",
         progname);
}

GLvoid
initgfx( GLvoid )
{
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glEnable( GL_DEPTH_TEST );

    fovy = 60.0;    /* field of view in Y */
    nearClip = 3.0;     /* Near clipping plane location */
    farClip  = 12.0;    /* Far clipping plane location */
    resetView();

    /* Turn on a default light */
    glEnable( GL_LIGHT0 );
}

GLvoid
toggleAmbient( GLvoid )
{
    /* Set up intensities for the light */
    static GLfloat    ambientIntensity[] = { 0.2, 0.0, 0.2, 1.0 };
    static GLfloat    defaultAmbient[] = { 0.0, 0.0, 0.0, 1.0 };

    static GLboolean useDefault = GL_TRUE;

    useDefault = !useDefault;

    if (useDefault)
         glLightfv( GL_LIGHT0, GL_AMBIENT, defaultAmbient );
    else 
         glLightfv( GL_LIGHT0, GL_AMBIENT, ambientIntensity );
}

GLvoid
toggleDiffuse( GLvoid )
{
    static GLfloat    diffuseIntensity[] = { 0.0, 0.1, 1.0, 1.0 };
    static GLfloat    defaultDiffuse[] = { 1.0, 1.0, 1.0, 1.0 };

    static GLboolean useDefault = GL_TRUE;

    useDefault = !useDefault;
    if (useDefault)
         glLightfv( GL_LIGHT0, GL_DIFFUSE, defaultDiffuse );
    else 
         glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseIntensity );
}

GLvoid
toggleSpecular( GLvoid )
{
    static GLfloat    specularIntensity[] = { 1.0, 0.0, 1.0, 1.0 };
    static GLfloat    defaultSpecular[] = { 1.0, 1.0, 1.0, 1.0 };

    static GLboolean useDefault = GL_TRUE;

    useDefault = !useDefault;
    if (useDefault)
         glLightfv( GL_LIGHT0, GL_SPECULAR, defaultSpecular );
    else 
         glLightfv( GL_LIGHT0, GL_SPECULAR, specularIntensity );
}

GLvoid 
keyboard( GLubyte key, GLint x, GLint y )
{
    switch (key) {
    case 'a':    /* toggle ambient intensity */
         toggleAmbient();
         break;
    case 'd':    /* toggle Diffuse intensity */
         toggleDiffuse();
         break;
    case 's':    /* toggle Specular intensity */
         toggleSpecular();
         break;
    case 'R':
         resetView();
         glutPostRedisplay();
         break;
    case KEY_ESC:   /* Exit whenever the Escape key is pressed */
         exit(0);
    }
    glutPostRedisplay();
}

GLvoid 
mouse( GLint button, GLint state, GLint x, GLint y )
{
    static GLint buttons_down = 0;

    if (state == GLUT_DOWN) {
         switch (button) {
         case GLUT_LEFT_BUTTON:
              action = MOVE_EYE;
              break;
         case GLUT_MIDDLE_BUTTON:
              action = TWIST_EYE;
              break;
         case GLUT_RIGHT_BUTTON:
              action = ZOOM;
              break;
         }

         /* Update the saved mouse position */
         xStart = x;
         yStart = y;
    } else {
         if (--buttons_down == 0)
              action = MOVE_NONE;
    }
}

GLvoid
motion( GLint x, GLint y )
{
    switch (action) {
    case MOVE_EYE:
         /* Adjust the eye position based on the mouse position */
         azimAngle += (GLdouble) (x - xStart);
         incAngle -= (GLdouble) (y - yStart);
         break;
    case TWIST_EYE:
         /* Adjust the eye twist based on the mouse position */
         twistAngle = fmod(twistAngle+(x - xStart), 360.0);
         break;
    case ZOOM:
         /* Adjust the eye distance based on the mouse position */
         distance -= (GLdouble) (y - yStart)/10.0;
         break;
    default:
         printf("unknown action %d\n", action);
    }
    
    /* Update the stored mouse position for later use */
    xStart = x;
    yStart = y;

    glutPostRedisplay();

}

void
resetView( GLvoid )
{
    distance = nearClip + (farClip - nearClip) / 2.0;
    twistAngle = 0.0;    /* rotation of viewing volume (camera) */
    incAngle = 60.0;
    azimAngle = 0.0;
    fovy = 60.0;    /* Field of view in Y angle */
}

GLvoid
reshape( GLsizei width, GLsizei height )
{
    GLdouble     aspect;

    glViewport( 0, 0, width, height );

    aspect = (GLdouble) width / (GLdouble) height;

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluPerspective( fovy, aspect, nearClip, farClip );
    glMatrixMode( GL_MODELVIEW );
}

void
polarView( GLfloat distance, GLfloat azimuth, GLfloat incidence,
              GLfloat twist)
{
    glTranslatef( 0.0, 0.0, -distance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -incidence, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 0.0, 1.0);
}

GLvoid
drawScene( GLvoid )
{
    /* Define a few materials properties */
    GLfloat   redAmbient[] = { 0.3, 0.1, 0.1, 1.0 };
    GLfloat   redDiffuse[] = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat   blueAmbient[] = { 0.1, 0.1, 0.3, 1.0 };
    GLfloat   blueDiffuse[] = { 0.0, 0.0, 1.0, 1.0 };
    GLfloat   yellowDiffuse[] = { 1.0, 1.0, 0.0, 1.0 };
    GLfloat   yellowEmission[] = { 0.6, 0.6, 0.0, 1.0 };
    GLfloat   defaultEmission[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat   whiteSpecular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat   greenSpecular[] = { 0.0, 1.0, 0.0, 1.0 };
    GLfloat   defaultSpecular[] = { 0.0, 0.0, 0.0, 1.0 };

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glPushMatrix();
        polarView( distance, azimAngle, incAngle, twistAngle );
        XYZaxes();

        glEnable( GL_LIGHTING );

        glMaterialfv( GL_FRONT, GL_EMISSION, defaultEmission );

        /* Set properties for a shiny red material,
         * with a green highlight */
        glMaterialfv( GL_FRONT, GL_AMBIENT, redAmbient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, redDiffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, greenSpecular );
        glMaterialf( GL_FRONT, GL_SHININESS, 128.0 );
        glPushMatrix()
            glTranslatef( -2.0, 1.5, 0.0 );
            glutSolidSphere( 0.7, 31, 31 );
        glPopMatrix();

        /* Set properties for a dull blue material with
         *   a small white highlight */
        glMaterialfv( GL_FRONT, GL_AMBIENT, blueAmbient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, blueDiffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, whiteSpecular );
        glMaterialf( GL_FRONT, GL_SHININESS, 20.0 );
        glPushMatrix();
            glTranslatef( 2.5, 0.0, 0.0 );
            glutSolidTorus( 0.25, 0.75, 16, 31 );
        glPopMatrix();

        /* Set properties for a yellow glowing material */
        glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, yellowDiffuse);
        glMaterialfv( GL_FRONT, GL_EMISSION, yellowEmission );
        glMaterialfv( GL_FRONT, GL_SPECULAR, defaultSpecular );

        glPushMatrix();
            glTranslatef( 0.0, 2.0, 2.0 );
            glutSolidCube( 0.5 );
        glPopMatrix();

        glDisable( GL_LIGHTING );

    glPopMatrix();

    glutSwapBuffers();
}

Notes:

When the diffuse, ambient and specular intensities are not the default, the objects are affected as follows:

Sphere -- the diffuse intensity is mostly green and the diffuse reflectance is all red, so no diffuse light is reflected. However, the ambient light intensity and the ambient reflectance both have some red and blue in them, so the object will look dark purple. There will be no specular highlight since the specular intensity is red and blue, and the specular reflectance is green.

Torus -- the diffuse reflectance is all blue, so the torus will reflect 100% blue diffuse intensity and none of the green, making it look blue. The ambient reflectance will be somewhat purple. The specular highlight should be purple.

Cube -- the diffuse reflectance is yellow (red and green) and the diffuse intensity is mostly blue, with a bit of green. The object should look green; however, the yellow emission forces it to be yellow. Because the ambient reflectance is the default (0.2, 0.2, 0.2), the edges that don't face the eye are grayish. It uses the default specular intensity (black) so there is no specular highlight.

Lab: Lighting Properties

This lab has two parts: tutorials, and programming.

Lighting Tutorials

  1. Change directory to siggraph_2001_demos/bin.

  2. Run ./lightposition, the lightposition tutorial program.

    • To reset the parameters at any time, right-click in the Command manipulation window and select Reset parameter(s).

    • To select a different model, right-click in the Screen-space view.

  3. Change the light position in by dragging on the XYZ coordinates in the pos array. Note the differences in the Screen-space view.

  4. Modify the eye position in the gluLookat arguments and observe the change. Then right-click in the Command manipulation window and select Swap lookat/position calls. What happens?

    • Swapping the calls demonstrates how lighting position is effected by the viewing transformation.

  5. Run the lightmaterial tutorial program again.

  6. Right-click in the Command manipulation window and select Light model parameter(s).

  7. Change the light's ambient, diffuse, emission and specular colors by dragging on the RGB contributions in the light_K arrays.

  8. Modify the ambient intensity of the scene by dragging on the RGB contributions in the lmodel_Ka array. Notice the interaction between this parameter and the light's ambient intensity above.

  9. Change the lighting model. You may need to change models or rotate the model (click and drag in the Screen-space view) to see the effects.

Setting Lighting Parameters

  1. Change directory to ../tutorial/exercises.

  2. Modify your solar10.c program to

    • Create a local light located at the sun

    • Enable two-sided lighting and zoom in on your scene to see the inside of the spheres. Make the core of the Earth be a molten red color, the core of the sun yellow, and the core of the moon gray

  3. * Change directory to ../tutorial/exercises.

  4. * Copy your material.c program to lighting.c and modify it to add a light and enable lighting for at least one of the objects in your scene.

    • Experiment with different light intensities or lighting models.

Notes:

* Indicates an optional laboratory exercise.



OpenGL Lighting Quick Reference

Lighting Checklist

Specifying Normals

OpenGL C Quick Reference

GLvoid glEnable( GL_LIGHTING )
GLvoid glDisable( GL_LIGHTING )

Turn lighting calculations on or off.


GLvoid glEnable( GL_LIGHT<n> )
GLvoid glDisable( GL_LIGHT<n> )

Enable or disable light source n.


GLvoid glLight[if]( GLenum light, GLenum pname, TYPE param )
GLvoid glLight[if]v( GLenum light, GLenum pname, TYPE *param )

Specify light source properties. light is GL_LIGHT<n>. pname is one of GL_SPOT_EXPONENT, GL_SPOT_CUTOFF, GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, and GL_QUADRATIC_ATTENUATION.


GLvoid glEnable( GL_NORMALIZE )
GLvoid glDisable( GL_NORMALIZE )

Enable or disable automatic normalization of normal vectors.


GLvoid glNormal3[bsidf]( TYPE nx, TYPE ny, TYPE nz )
GLvoid glNormal3[bsidf]v( const TYPE *v )

Specify a normal vector, which becomes the normal for all successive vertices until the next call to glNormal3*(). Causes the color of each vertex to be computed if lighting is active.


GLvoid glMaterial[if]( GLenum face, GLenum pname, TYPE param )
GLvoid glMaterial[if]v( GLenum face, GLenum pname, TYPE *param )

Specify material reflection properties. face can be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK. For glMaterial[if], pname must be GL_SHININESS. For glMaterial[if]v, pname can be GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION, GL_SHININESS, GL_AMBIENT_AND_DIFFUSE, or GL_COLOR_INDEXES.


GLvoid glEnable( GL_COLOR_MATERIAL )
GLvoid glDisable( GL_COLOR_MATERIAL )

Enable or disable fast material changes via glColorMaterial().


GLvoid glColorMaterial( GLenum face, GLenum property ) 

Makes all subsequent glColor*() calls affect material properties. Often faster than using glMaterial*() when changing only one property rapidly. Valid face values are the same as for glMaterial*. property can be GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION, or GL_AMBIENT_AND_DIFFUSE.

mElite 07: Let's switch on the light

Download the new version of eliteutils.h and eliteutils.c. Here is the new version of drawEliteObject (called drawEliteObject2) used to draw coriolis using lighting with the Non-Photorealistic Rendering (NPR).

Our new version of mElite has to use lighting. New features to add (emphasised in the the proposed solution code with the word "NEW" in the comments) are:

>>Here<< is a possible solution. You can compile it with the Makefile used for melite6b.c. Obviously, as for the previous version, for compiling you need camera.cpp, camera.h, coriolis.h, eliteutils.h, and eliteutils.c in the same directory.