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:
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
normals
2 to eachvertex
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 light
1 - effective location of the
viewpoint
- level of
- 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;
}
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 vectors
2 for Each vertex
of Every Object
An object's normals
2 determine its orientation relative to the light sources. In the example, these normals
2 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 pointer
3 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 vector
4 \((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 inhomogeneous object coordinates
.
OpenGL
attenuates a light source by multiplying the contribution of that source by an attenuation factor
where
- \(d\) is distance between
light
's position and thevertex
- \(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
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 matrix
5 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;
}
2 mouse clicks
7 mouse clicks
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 light
1
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;
}
0 Mouse clicks
1 Mouse clicks
4 Mouse clicks
5 Mouse clicks