Skip to content

43. Real World and Opengl Lighting

Dated: 13-07-2025

When we look at a physical surface, our eye's perception of the color depends on the distribution of photon energies that arrive and trigger our cone cells. Those photons come from a light source or combination of sources, some of which are absorbed and some are reflected by the surface. In addition, different surfaces may have very different properties - some are shiny and preferentially reflect light in certain directions, while others scatter incoming light equally in all directions. Most surfaces are somewhere in between.

The OpenGL lighting model considers the lighting to be divided into four independent components:

  • emissive1
  • ambient1
  • diffuse1
  • specular1

All four components are computed independently and then added together.

A Simple Example: Rendering a Lit Sphere

These are the steps required to add lighting to our scene.

  • Define normals2 to each vertex of the object (helps in determining orientation with relation to light source).
  • Create, select, and position one or more light sources.
  • Create and select a lighting model which determines
    • level of global ambient light1
    • effective location of the viewpoint
  • Define material properties for the objects in the scene.
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

void init(void) {
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glutSolidSphere(1.0, 20, 16);
    glFlush();
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if (w <= h)
        glOrtho(-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho(-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();

    return 0;
}

cs602_i_43_1.png

Code Result

RGBA color mode is preferred in comparison to color index mode because lighting capabilities of color index are limited

Steps in Details

Defining normal vectors2 for Each vertex of Every Object

An object's normals2 determine its orientation relative to the light sources. In the example, these normals2 are defined in glutSolidSphere().

Create, Position, and Enable One or More light Sources

The example uses only one white light source specified by glLightfv() call. We can introduce at least 8 different light sources with different colors (the default for OpenGL is black). We also need to call glEnable() with GL_LIGHTING as a parameter to prepare OpenGL to perform lighting calculations.

Select a Lighting Model

In the example, the only element of the lighting model that's defined explicitly is the global ambient light. This example uses the default settings, i.e.

  • A viewer with infinite distance
  • One sided lighting

Define Material Properties for the Objects in the Scene

An object's material properties determine how it reflects light and therefore what material it seems to be made of. In this example, we used only 2 material properties, specified with glMaterialfv().

Creating light Sources

  • void glLighti(GLenum light, GLenum pname, TYPE param);
  • void glLightf(GLenum light, GLenum pname, TYPE param);
  • void glLightiv(GLenum light, GLenum pname, TYPE *param);
  • void glLightfv(GLenum light, GLenum pname, TYPE *param);

These functions create lights ranging from GL_LIGHT0 to GL_LIGHT7.
The characteristics of the light being set is defined by pname.
The param indicates the value to which pname is set. For the vectorized version, a pointer3 to the vector is used.

Parameter name Default Value Meaning
GL_AMBIENT \((0.0, 0.0, 0.0, 1.0)\) ambient RGBA intensity of light
GL_DIFFUSE \((1.0, 1.0, 1.0, 1.0)\) diffuse RGBA intensity of light
GL_SPECULAR \((1.0, 1.0, 1.0, 1.0)\) specular RGBA intensity of light
GL_POSITION \((0.0, 0.0, 1.0, 0.0)\) \((x, y, z, w)\) position of light
GL_SPOT_DIRECTION \((0.0, 0.0, -1.0)\) \((x, y, z)\) direction of spotlight
GL_SPOT_EXPONENT \(0.0\) spotlight exponent
GL_SPOT_CUTOFF \(180.0\) spotlight cutoff angle
GL_CONSTANT_ATTENUATION \(1.0\) constant attenuation factor
GL_LINEAR_ATTENUATION \(0.0\) linear attenuation factor
GL_QUADRATIC_ATTENUATION \(0.0\) quadratic attenuation factor

Example

Defining colors and position of a light source.

GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);

Remember to turn each light on using glEnable().

The alpha component of these colors is not used until blending is enabled.

Position and Attenuation

  • Parallel lights (sources are at infinite distance) are directional
  • Sources of light within the scene are positional

we supply a 4 valued vector4 \((x, y, z, w)\) for GL_POSITION.

  • If \(w = 0\) then the corresponding light is directional one and \((x, y, z)\) describe its direction.
  • If \(w \ne 0\) then the corresponding light is positional one and \((x, y, z)\) describe its direction in homogeneous object coordinates.

OpenGL attenuates a light source by multiplying the contribution of that source by an attenuation factor

\[\text{attenuation factor} = \frac 1 {k_c + k_l d + k_q d^2}\]

where

  • \(d\) is distance between light's position and the vertex
  • \(k_c\) is GL_CONSTANT_ATTENUATION
  • \(k_l\) is GL_LINEAR_ATTENUATION
  • \(k_q\) is GL_QUADRATIC_ATTENUATION
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);

Spotlights

cs602_e_43_1.svg

Converting a positional light into a spotlight

Controlling a Light's Position and Direction

We can manipulate a light source's position or direction by changing the contents of the modelview matrix.5

The projection matrix5 has no effect on a light's position or direction.

Keeping the light Stationary

The code inside init() and reshape() could be

glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if (w <= h)
    glOrtho(-1.5, 1.5, -1.5 * h / w, 1.5 * h / w, -10.0, 10.0);
else
    glOrtho(-1.5 * w / h, 1.5 * w / h, -1.5, 1.5, -10.0, 10.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

/* later in init() */
GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, position);

Independently Moving the light

Now suppose we want to rotate or translate the light position so that the light moves relative to a stationary object.

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

static int spin = 0;

void init(void) {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH); 
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
}
/* Here is where the light position is reset after the modeling
* transformation (glRotated) is called. This places the
* light at a new position in world coordinates. The cube
* represents the position of the light.
*/
void display(void) {
    GLfloat position[] = { 0.0, 0.0, 1.5, 1.0 };
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glTranslatef(0.0, 0.0, -5.0);
    glPushMatrix();
    glRotated((GLdouble) spin, 1.0, 0.0, 0.0);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glTranslated(0.0, 0.0, 1.5);
    glDisable(GL_LIGHTING);
    glColor3f(0.0, 1.0, 1.0);
    glutWireCube(0.1);
    glEnable(GL_LIGHTING);
    glPopMatrix();
    glutSolidTorus (0.275, 0.85, 8, 15);
    glPopMatrix();
    glFlush();
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(40.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void mouse(int button, int state, int x, int y) {
    switch (button) {
        case GLUT_LEFT_BUTTON:
            if (state == GLUT_DOWN) {
                spin = (spin + 30) % 360;
                glutPostRedisplay();
            }
            break;

        default:
            break;
    }
}

int main(int argc, char** argv) {

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMainLoop();

    return 0;
}

cs402_i_43_2.png

2 mouse clicks

cs602_i_43_3.png

7 mouse clicks

cs602_i_43_4.png

11 mouse clicks

Selecting a Lighting Model

The OpenGL notion of a lighting model has three components

  • The global ambient light intensity
  • Whether the viewpoint position is local to the scene or whether it should be considered to be an infinite distance away
  • Whether lighting calculations should be performed differently for both the front and back faces of objects

The functions OpenGL provides are following

  • void glLightModeli(GLenum pname, TYPE param);
  • void glLightModelf(GLenum pname, TYPE param);
  • void glLightModeliv(GLenum pname, TYPE *param);
  • void glLightModelfv(GLenum pname, TYPE *param);
Parameter name Default Value Meaning
GL_LIGHT_MODEL_AMBIENT \((0.2, 0.2, 0.2, 1.0)\) ambient RGBA intensity of the entire scene
GL_LIGHT_MODEL_LOCAL_VIEWER \(0.0\) or GL_FALSE how specular reflection angles are computed
GL_LIGHT_MODEL_TWO_SIDE \(0.0\) or GL_FALSE choose between one sided or two sided lighting

Global Ambient light1

GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

Enabling Lighting

With OpenGL, we need to explicitly enable (or disable) lighting.

glEnable(GL_LIGHTING);
glDisable(GL_LIGHTING);

Enabling one specific light:

glEnable(GL_LIGHT0);

Defining Material Properties

  • void glMateriali(GLenum face, GLenum pname, TYPE param);
  • void glMaterialf(GLenum face, GLenum pname, TYPE param);
  • void glMaterialiv(GLenum face, GLenum pname, TYPE *param);
  • void glMaterialfv(GLenum face, GLenum pname, TYPE *param);
Parameter Name Default Value Meaning
GL_AMBIENT \((0.2, 0.2, 0.2, 1.0)\) ambient color of material
GL_DIFFUSE \((0.8, 0.8, 0.8, 1.0)\) diffuse color of material
GL_AMBIENT_AND_DIFFUSE ambient and diffuse color of material
GL_SPECULAR \((0.0, 0.0, 0.0, 1.0)\) specular color of material
GL_SHININESS \(0.0\) specular exponent
GL_EMISSION \((0.0, 0.0, 0.0, 1.0)\) emissive color of material
GL_COLOR_INDEXES \((0, 1, 1)\) ambient, diffuse and specular color indices

Diffuse and Ambient Reflection

GLfloat mat_amb_diff[] = { 0.1, 0.5, 0.8, 1.0 };
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_amb_diff);

Specular Reflection

GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat low_shininess[] = { 5.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);

Emission

By specifying an RGBA color for GL_EMISSION, we can make an object appear to be giving off light of that color.

GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0};
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);

Changing Material Properties

void glColorMaterial(GLenum face, GLenum mode);

OpenGL does not maintain separate mode variables for each face. After calling glColorMaterial(), we need to call glEnable() with GL_COLOR_MATERIAL as the parameter.

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

GLfloat diffuseMaterial[4] = { 0.5, 0.5, 0.5, 1.0 };

void init(void) {
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; 
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialf(GL_FRONT, GL_SHININESS, 25.0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glColorMaterial(GL_FRONT, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glutSolidSphere(1.0, 20, 16);
    glFlush();
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if (w <= h)
        glOrtho(-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void mouse(int button, int state, int x, int y) {
    switch (button) {
        case GLUT_LEFT_BUTTON:
            if (state == GLUT_DOWN) { /* change red */
                diffuseMaterial[0] += 0.1;

                if (diffuseMaterial[0] > 1.0)
                    diffuseMaterial[0] = 0.0;

                glColor4fv(diffuseMaterial);
                glutPostRedisplay();
            }
            break;

        case GLUT_MIDDLE_BUTTON:
            if (state == GLUT_DOWN) { /* change green */
                diffuseMaterial[1] += 0.1;

                if (diffuseMaterial[1] > 1.0)
                    diffuseMaterial[1] = 0.0;

                glColor4fv(diffuseMaterial);
                glutPostRedisplay();
            }
            break;

        case GLUT_RIGHT_BUTTON:
            if (state == GLUT_DOWN) { /* change blue */
                diffuseMaterial[2] += 0.1;

                if (diffuseMaterial[2] > 1.0)
                    diffuseMaterial[2] = 0.0;

                glColor4fv(diffuseMaterial);
                glutPostRedisplay();
            }
            break;

        default:
            break;
    }
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMainLoop();

    return 0;
}

cs602_i_43_5.png

0 Mouse clicks

cs602_i_43_6.png

1 Mouse clicks

cs602_i_43_7.png

4 Mouse clicks

cs602_i_43_8.png

5 Mouse clicks

References


  1. Read more about lighting types

  2. Read more about surface normals

  3. Read more about pointers

  4. Read more about vectors

  5. Read more about matrices