JMU
Lighting in OpenGL
An Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Parts of the Process
Specifying Normal Vectors
Specifying Normal Vectors (cont.)

An Example

openglexamples/lighting/lighting.c (Fragment: normals)
        
// Normals
static GLfloat normals[][3] = {{ 0.0, 0.0, 1.0}, // front
                               { 1.0, 0.0, 0.0}, // right
                               { 0.0,-1.0, 0.0}, // bottom
                               { 0.0, 0.0,-1.0}, // back
                               {-1.0, 0.0, 0.0}, // left
                               { 0.0, 1.0, 0.0}  // top
                               };

  glBegin(GL_POLYGON);

  // One normal for all vertices
  glNormal3fv(normals[face]);

  // One color for all the vertices
  glColor4fv(colorPurple);

  // The vertices
  for (int i = 0; i < 4; i++) {
    glVertex3fv(vertices[faces[face][i]]);
  }

  glEnd();
        
Specifying Material Properties
Specifying Material Properties (cont.)

An Example

openglexamples/lighting/lighting.c (Fragment: material)
          // The color of the material for specular reflection
  glMaterialfv(GL_FRONT, GL_SPECULAR, colorWhite);

  // The exponent of the specular reflection model
  glMaterialf(GL_FRONT, GL_SHININESS, 50.0);
        
Some Materials
openglexamples/lighting/materials.h
        #ifndef edu_jmu_cs_Materials_h
#define edu_jmu_cs_Materials_h

int BRASS         =  0;
int BRONZE        =  1;
int CHROME        =  2;
int COPPER        =  3;
int GOLD          =  4;
int PEWTER        =  5;
int SILVER        =  6;
int JADE          =  7;
int OBSIDIAN      =  8;
int PEARL         =  9;
int RUBY          = 10;
int TURQUOISE     = 11;
int BLACK_PLASTIC = 12;
int BLACK_RUBBER  = 13;

int LAST_MATERIAL = 14;



float SPECULAR_EXPONENTS[] = 
{
   27.897400, // BRASS
   25.600000, // BRONZE
   76.800003, // CHROME
   12.800000, // COPPER
   51.200001, // GOLD
   09.846150, // PEWTER
   51.200001, // SILVER
   76.800003, // EMERALD
   12.800000, // JADE
   38.400002, // OBSIDIAN
   11.264000, // PEARL
   76.800003, // RUBY
   12.800000, // TURQUOISE
   32.000000, // BLACK_PLASTIC
   10.000000  // BLACK_RUBBER
};


float MATERIAL_COLORS[][3][4] = 
{
   // BRASS
   {
      {0.329412, 0.223529, 0.027451, 1.000000}, // Ambient RGBA
      {0.780392, 0.568627, 0.113725, 1.000000}, // Diffuse RGBA
      {0.992157, 0.941176, 0.807843, 1.000000}  // Specular RGBA
   },

   // BRONZE
   {
      {0.212500, 0.127500, 0.054000, 1.000000},
      {0.714000, 0.428400, 0.181440, 1.000000},
      {0.393548, 0.271906, 0.166721, 1.000000}
   },
   
   // CHROME
   {
      {0.250000, 0.250000, 0.250000, 1.000000},
      {0.400000, 0.400000, 0.400000, 1.000000},
      {0.774597, 0.774597, 0.774597, 1.000000}
   },
   
   // COPPER
   {
      {0.191250, 0.073500, 0.022500, 1.000000},
      {0.703800, 0.270480, 0.082800, 1.000000},
      {0.256777, 0.137622, 0.086014, 1.000000}
   },

   // GOLD
   {
      {0.247250, 0.199500, 0.074500, 1.000000},
      {0.751640, 0.606480, 0.226480, 1.000000},
      {0.628281, 0.555802, 0.366065, 1.000000}
   },

   // PEWTER
   {
      {0.105882, 0.058824, 0.113725, 1.000000},
      {0.427451, 0.470588, 0.541176, 1.000000},
      {0.333333, 0.333333, 0.521569, 1.000000}
   },

   // SILVER
   {
      {0.192250, 0.192250, 0.192250, 1.000000},
      {0.507540, 0.507540, 0.507540, 1.000000},
      {0.508273, 0.508273, 0.508273, 1.000000}
   },

   // EMERALD
   {
      {0.021500, 0.174500, 0.021500, 0.550000},
      {0.075680, 0.614240, 0.075680, 0.550000},
      {0.633000, 0.727811, 0.633000, 0.550000}
   },

   // JADE
   {
      {0.135000, 0.222500, 0.157500, 0.950000},
      {0.540000, 0.890000, 0.630000, 0.950000},
      {0.316228, 0.316228, 0.316228, 0.950000}
   },

   // OBSIDIAN
   {
      {0.053750, 0.050000, 0.066250, 0.820000},
      {0.182750, 0.170000, 0.225250, 0.820000},
      {0.332741, 0.328634, 0.346435, 0.820000}
   },

   // PEARL
   {
      {0.250000, 0.207250, 0.207250, 0.922000},
      {1.000000, 0.829000, 0.829000, 0.922000},
      {0.296648, 0.296648, 0.296648, 0.922000}
   },

   // RUBY
   {
      {0.174500, 0.011750, 0.011750, 0.550000},
      {0.614240, 0.041360, 0.041360, 0.550000},
      {0.727811, 0.626959, 0.626959, 0.550000}
   },

   // TURQUOISE
   {
      {0.100000, 0.187250, 0.174500, 0.800000},
      {0.396000, 0.741510, 0.691020, 0.800000},
      {0.297254, 0.308290, 0.306678, 0.800000}
   },

   // BLACK_PLASTIC
   {
      {0.000000, 0.000000, 0.000000, 1.000000},
      {0.010000, 0.010000, 0.010000, 1.000000},
      {0.500000, 0.500000, 0.500000, 1.000000}
   },

   // BLACK_RUBBER
   {
      {0.020000, 0.020000, 0.020000, 1.000000},
      {0.010000, 0.010000, 0.010000, 1.000000},
      {0.400000, 0.400000, 0.400000, 1.000000}
   }
};


#endif
        
Creating and Positioning Light Sources
Light Sources (cont.)
Light Sources (cont.)

An Example

openglexamples/lighting/lighting.c (Fragment: lightsource)
          GLfloat light0Position[] = { 0.0, 0.0, 2.0, 1.0 };
  glLightfv(GL_LIGHT0, GL_POSITION, light0Position);

  glLightfv(GL_LIGHT0, GL_DIFFUSE, colorWhite);

  glLightfv(GL_LIGHT0, GL_SPECULAR, colorWhite);
        
Specifying the Lighting Model
Specifying the Lighting Model (cont.)

An Example

openglexamples/lighting/lighting.c (Fragment: lightingmodel)
          glShadeModel(GL_SMOOTH);

  // Intensity of ambient light (RGBA)
  GLfloat lightingmodelAmbient[] = { 0.2, 0.2, 0.2, 1.0 };
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightingmodelAmbient);

  // Calculation of specular reflection angles (local or infinite viewer?)
  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);

  // Two-sided lighting?
  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
        
Enabling Lighting and Lights
Enabling Lighting and Lights (cont.)

An Example

openglexamples/lighting/lighting.c (Fragment: enable)
          glEnable(GL_LIGHTING);     // Enable lighting
  glEnable(GL_LIGHT0);       // Enable light number 0
  glEnable(GL_DEPTH_TEST);   // Enable depth buffering
        
A Complete Example

Using a Sphere

openglexamples/lighting/sphere.c
        #include <GL/glut.h>
#include "materials.h"

/**
 * \file
 * A simple OpenGL application that demonstrates lighting
 * a sphere
 *
 * Click the:
 *   Left button to cycle through different light positions
 *   Middle button to cycle through different materials
 *   Right button to translate the sphere to the right
 *
 * Note: Double buffering is not used so the screen may flicker
 *
 * Compile under MinGW with:
 *   g++ -o sphere -Wall sphere.c -lglut -lGL -lGLU
 *
 * @author  Prof. David Bernstein, James Madison University
 */

int window;

static GLint axis = 0;
static GLfloat shininess = 50.0;

static GLfloat light0Position[] = { 2.0, 0.0, 0.0, 1.0 };

//Colors
static GLfloat colorBlue[] = { 0.0, 0.0, 1.0, 1.0 };
static GLfloat colorGold[] = { 0.761, 0.631, 0.302, 1.000 };  // JMU Gold
static GLfloat colorGreen[] = { 0.0, 1.0, 0.0, 1.0 };
static GLfloat colorPurple[] = { 0.271, 0.000, 0.518, 1.000 };  // JMU Purple
static GLfloat colorRed[] = { 1.0, 0.0, 0.0, 1.0 };
static GLfloat colorWhite[] = { 1.0, 1.0, 1.0, 1.0 };

static int material = 0;

/**
 * Create a window.
 *
 * @param title   The title
 * @param width   The width (in pixels)
 * @param height  The height (in pixels)
 * @param x       The x position
 * @param y       The y position
 * @return        The window id
 */
int createWindow(char* title, int width, int height, int x, int y) {
  int id;

  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(width, height);
  glutInitWindowPosition(x, y);
  id = glutCreateWindow(title);

  return id;
}

/**
 * Perform OpenGL initializations.
 */
void init() {
  glClearColor(colorGold[0], colorGold[1], colorGold[2], colorGold[3]);

  // The color of the material for specular reflection
  glMaterialfv(GL_FRONT, GL_SPECULAR, colorWhite);

  // The exponent of the specular reflection model
  glMaterialf(GL_FRONT, GL_SHININESS, shininess);

  glShadeModel(GL_SMOOTH);

  // Intensity of ambient light (RGBA)
  GLfloat lightingmodelAmbient[] = { 0.2, 0.2, 0.2, 1.0 };
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightingmodelAmbient);

  // Calculation of specular reflection angles (local or infinite viewer?)
  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);

  // Two-sided lighting?
  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

  glLightfv(GL_LIGHT0, GL_POSITION, light0Position);

  glLightfv(GL_LIGHT0, GL_DIFFUSE, colorWhite);

  glLightfv(GL_LIGHT0, GL_SPECULAR, colorWhite);

  glEnable(GL_LIGHTING);     // Enable lighting
  glEnable(GL_LIGHT0);       // Enable light number 0
  glEnable(GL_DEPTH_TEST);   // Enable depth buffering
}

/**
 * The mouse callback (i.e., the function that is called
 * each time a mouse button is pressed or released).
 *
 * @param button The button (e.g., GLUT_LEFT_BUTTON)
 * @param state  The state (e.g., GLUT_UP or GLUT_DOWN)
 * @param x      The x-position of the mouse
 * @param y      The y-position of the mouse
 */
void mouseClicked(int button, int state, int x, int y) {
  if (state == GLUT_DOWN) {
    if (button == GLUT_LEFT_BUTTON) {
      light0Position[axis] = 0.0;
      axis = (axis + 1) % 3;
      light0Position[axis] = 2.0;

      glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
    } else if (button == GLUT_RIGHT_BUTTON) {
      material = (material + 1) % LAST_MATERIAL;
      glMaterialfv(GL_FRONT, GL_AMBIENT, MATERIAL_COLORS[material][0]);
      glMaterialfv(GL_FRONT, GL_DIFFUSE, MATERIAL_COLORS[material][1]);
      glMaterialfv(GL_FRONT, GL_SPECULAR, MATERIAL_COLORS[material][2]);
      glMaterialf(GL_FRONT, GL_SHININESS, SPECULAR_EXPONENTS[material]);
    } else if (button == GLUT_MIDDLE_BUTTON) {
      glMatrixMode(GL_MODELVIEW);
      glTranslatef(0.10, 0.0, 0.0);
    }
  }

  glutPostRedisplay();
}

/**
 * Display/render the OpenGL window
 */
void display() {
  glMatrixMode(GL_MODELVIEW);

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Create the content
  glutSolidSphere(1.0, 20, 16);
  //glutSolidTeapot(1.0);

  glFlush();
}

/**
 * The reshape callback
 *
 * @param w   The width
 * @param h   The height
 */
void reshape(int w, int h) {
  GLfloat aspect;

  glViewport(0, 0, (GLsizei) w, (GLsizei) h);

  glMatrixMode(GL_PROJECTION);

  glLoadIdentity();

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

/**
 * The entry point of the application.
 *
 * This function contains calls to GLUT.
 *
 * @param argc  The number of command line arguments
 * @param argv  The array of command line arguments
 * @return      A status code
 */
int main(int argc, char** argv) {
  glutInit(&argc, argv);

  window = createWindow("Sphere Lighting", 640, 480, 0, 0);

  // Callbacks
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouseClicked);

  init();

  glutMainLoop();
}
        
Another Complete Example

Using a File Containing the Model

openglexamples/lighting/solidmodel.c
        #include <stdio.h>
#include <GL/glut.h>
#include <math.h>

/**
 * \file
 * A simple OpenGL application that demonstrates lighting
 * a solid model
 *
 * Click the:
 *   Left button to cycle through different light positions
 *
 * Press the:
 *   x or X key to rotate around the x-axis
 *   y or Y key to rotate around the y-axis
 *   z or Z key to rotate around the z-axis
 *
 *
 * Compile under Linux with:
 *   g++ -o solidmodel -Wall solidmodel.c -lglut -lGL -lGLU
 *
 * @author  Prof. David Bernstein, James Madison University
 */

int window;

static GLint axis = 0;
static GLfloat shininess = 50.0;
static GLfloat light0Position[4];

static GLfloat colorWhite[] = { 1.0, 1.0, 1.0, 1.0 };

static int count;

static GLfloat *x, *y, *z;
static GLfloat *nx, *ny, *nz;
static GLfloat *r, *g, *b;

static GLfloat color[3];
static GLfloat max[3], min[3];
static GLfloat left, right, bottom, top, znear, zfar;

static GLfloat theta[] = { 0.0, 0.0, 0.0 };

/**
 * Create a window.
 *
 * @param title   The title
 * @param width   The width (in pixels)
 * @param height  The height (in pixels)
 * @param x       The x position
 * @param y       The y position
 * @return        The window id
 */
int createWindow(char* title, int width, int height, int x, int y) {
  int id;

  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(width, height);
  glutInitWindowPosition(x, y);
  id = glutCreateWindow(title);

  return id;
}

/**
 * Perform OpenGL initializations.
 */
void init() {
  glClearColor(colorWhite[0], colorWhite[1], colorWhite[2], colorWhite[3]);
  glMaterialfv(GL_FRONT, GL_SPECULAR, colorWhite);
  glMaterialf(GL_FRONT, GL_SHININESS, shininess);
  glShadeModel(GL_SMOOTH);
  GLfloat lightingmodelAmbient[] = { 0.2, 0.2, 0.2, 1.0 };
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightingmodelAmbient);
  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
  glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_FALSE);
  glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, colorWhite);
  glLightfv(GL_LIGHT0, GL_SPECULAR, colorWhite);

  glEnable(GL_LIGHTING);     // Enable lighting
  glEnable(GL_LIGHT0);       // Enable light number 0
  glEnable(GL_DEPTH_TEST);   // Enable depth buffering
}

/**
 * The mouse callback (i.e., the function that is called
 * each time a mouse button is pressed or released).
 *
 * @param button The button (e.g., GLUT_LEFT_BUTTON)
 * @param state  The state (e.g., GLUT_UP or GLUT_DOWN)
 * @param x      The x-position of the mouse
 * @param y      The y-position of the mouse
 */
void mouseClicked(int button, int state, int x, int y) {
  if (state == GLUT_DOWN) {
    if (button == GLUT_LEFT_BUTTON) {
      light0Position[axis] = 0.0;
      axis = (axis + 1) % 3;
      light0Position[axis] = max[axis];

      glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
    }
  }
  glutPostRedisplay();
}

/**
 * Display/render the OpenGL window.
 */
void display() {
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  // Center
  glTranslatef(-(left + right) / 2.0, -(top + bottom) / 2.0,
               -(znear + zfar) / 2.0);

  // Rotate
  glRotatef(theta[0], 1.0, 0.0, 0.0);
  glRotatef(theta[1], 0.0, 1.0, 0.0);
  glRotatef(theta[2], 0.0, 0.0, 1.0);

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBegin(GL_TRIANGLES);
  for (int i = 0; i < count; i++) {
    int k;

    k = 3 * i;

    // Define the material properties for this triangle
    color[0] = r[i]; color[1] = g[i]; color[2] = b[i];
    glMaterialfv(GL_FRONT, GL_AMBIENT, color);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
    glMaterialfv(GL_FRONT, GL_SPECULAR, color);
    glMaterialf(GL_FRONT, GL_SHININESS, shininess);

    // Define the vertices and normals for this triangle
    glNormal3f(nx[k], ny[k], nz[k]);
    glVertex3f(x[k], y[k], z[k]);

    glNormal3f(nx[k + 1], ny[k + 1], nz[k + 1]);
    glVertex3f(x[k + 1], y[k + 1], z[k + 1]);

    glNormal3f(nx[k + 2], ny[k + 2], nz[k + 2]);
    glVertex3f(x[k + 2], y[k + 2], z[k + 2]);
  }
  glEnd();

  glFlush();
  glutSwapBuffers();
}

/**
 * The reshape callback
 *
 * @param width    The width
 * @param height   The height
 */
void reshape(int width, int height) {
  GLfloat x, y, z;

  glViewport(0, 0, (GLsizei) width, (GLsizei) height);

  glMatrixMode(GL_PROJECTION);

  glLoadIdentity();

  x = fmax(abs(left), abs(right));
  y = fmax(abs(top), abs(bottom));
  z = fmax(abs(znear), abs(zfar));

  glOrtho(-2 * x, 2 * x, -2 * y, 2 * y, -2 * z, 2 * z);
}

/**
 * Read the data from a file.
 *
 * @param fileName   The name of the file
 */
void readData(const char* fileName) {
  char s[80];
  FILE *in;
  float v[3];
  int color;

  for (int i = 0; i < 3; i++) {
    min[i] = 99999999.;
    max[i] = -99999999.;
  }

  in = fopen(fileName, "r");

  // Read the number of triangles
  fscanf(in, "%d", &count);

  // Initialize the arrays
  // NOTE: This memory is never returned to the free store!
  r = new GLfloat[count];
  g = new GLfloat[count];
  b = new GLfloat[count];

  x = new GLfloat[count * 3];
  y = new GLfloat[count * 3];
  z = new GLfloat[count * 3];

  nx = new GLfloat[count * 3];
  ny = new GLfloat[count * 3];
  nz = new GLfloat[count * 3];

  for (int k = 0; k < count * 3; k++) {
    x[k] = 0.0;
    y[k] = 0.0;
    z[k] = 0.0;
    nx[k] = 0.0;
    ny[k] = 0.0;
    nz[k] = 0.0;
  }

  // Read the triangles
  for (int k = 0; k < count; k++) {
    // Triangle indicator
    fscanf(in, "%s", s);

    // Front Color
    fscanf(in, "%d", &color);
    r[k] = (float) color / 255.0;
    fscanf(in, "%d", &color);
    g[k] = (float) color / 255.0;
    fscanf(in, "%d", &color);
    b[k] = (float) color / 255.0;

    // Back Color (which is ignored)
    fscanf(in, "%d", &color);
    fscanf(in, "%d", &color);
    fscanf(in, "%d", &color);

    // The vertices and normals
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3; j++) {
        fscanf(in, "%f", &(v[j]));  // Vertex
        if (v[j] < min[j])
          min[j] = v[j];
        if (v[j] > max[j])
          max[j] = v[j];
      }
      x[3 * k + i] = v[0];
      y[3 * k + i] = v[1];
      z[3 * k + i] = v[2];

      fscanf(in, "%f", &(nx[3 * k + i]));
      fscanf(in, "%f", &(ny[3 * k + i]));
      fscanf(in, "%f", &(nz[3 * k + i]));
    }
  }

  left = min[0];
  right = max[0];
  bottom = min[1];
  top = max[1];
  znear = min[2];
  zfar = max[2];

  light0Position[0] = max[0];
  light0Position[1] = max[1];
  light0Position[2] = max[2];
  light0Position[3] = 1.0;
}

/**
 * The keyboard callback
 *
 * @param key   The key that was pressed
 * @param x     The x-position of the mouse when the key was pressed
 * @param y     The y-position of the mouse when the key was pressed
 */
void keyboardFunc(unsigned char key, int x, int y) {
  if (key == 'x')
    theta[0] -= 10.00;
  else if (key == 'X')
    theta[0] += 10.00;
  else if (key == 'y')
    theta[1] -= 10.00;
  else if (key == 'Y')
    theta[1] += 10.00;
  else if (key == 'z')
    theta[2] -= 10.00;
  else if (key == 'Z')
    theta[2] += 10.00;
  else if (key == ' ')
    theta[0] = theta[1] = theta[2] = 0.0;

  // Request a callback to the DisplayFunc
  glutPostRedisplay();
}

/**
 * The entry point of the application.
 *
 * This function contains calls to GLUT.
 *
 * @param argc  The number of command line arguments
 * @param argv  The array of command line arguments
 * @return      A status code
 */
int main(int argc, char** argv) {
  glutInit(&argc, argv);

  if (argc == 1)
    readData("church.txt");
  else
    readData(argv[1]);

  window = createWindow("Solid Model Lighting", 640, 640, 0, 0);

  // Callbacks
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouseClicked);
  glutKeyboardFunc(keyboardFunc);

  init();

  glutMainLoop();
}