12. OpenGL Materials and Lighting (Version 2.0)

Introduction

Lighting and materials go hand-in-hand when it comes to creating visually stunning 3D objects in OpenGL. While setting up lights allows us to illuminate objects, defining materials determines how those objects interact with the light.

In this tutorial, we’ll dive into adding colours to objects and manipulating material properties like shininess and light reflectivity. We’ll also explore the interplay between coloured lights and materials to achieve realistic effects.

Light Basics

Before we get started, it’s important to understand how light behaves differently from paint. While mixing paints follows the rules of subtraction (e.g., red + green = brown), light follows the rules of addition (e.g., red + green = yellow). Here are some examples to clarify the difference:

Paint:
Red + Green = Brown
Blue + Yellow = Green
White + Blue = Light Blue
Red + White = Light Red

Lights:
Red + Green = Yellow
Blue + Yellow = White
White + Blue = Light Blue
Red + White = Yellow

This difference significantly impacts how coloured lights interact with coloured objects in OpenGL.

Usage

OpenGL allows us to control how materials interact with lights through properties such as diffuse, specular, and emissive lighting. Additionally, the shininess factor defines the reflectivity of an object.

Here’s an overview of key material properties:
– **Diffuse:** Determines the base colour of the object when exposed to direct light.
– **Specular:** Controls the shininess and the amount of light reflection.
– **Emissive:** Adds self-illumination, making the object appear as if it’s glowing.

The shininess factor ranges from 0 to 128, where 0 is the shiniest.

Coding

To start, let’s define the colours and properties for materials and lights:

GLfloat redDiffuseMaterial[] = {1.0, 0.0, 0.0}; // Red material
GLfloat whiteSpecularMaterial[] = {1.0, 1.0, 1.0}; // White specular material
GLfloat greenEmissiveMaterial[] = {0.0, 1.0, 0.0}; // Green emissive material

GLfloat whiteSpecularLight[] = {1.0, 1.0, 1.0}; // White light specular
GLfloat blackAmbientLight[] = {0.0, 0.0, 0.0}; // Ambient light (black)
GLfloat whiteDiffuseLight[] = {1.0, 1.0, 1.0}; // White diffuse light

GLfloat blankMaterial[] = {0.0, 0.0, 0.0}; // Blank material for reset
GLfloat mShininess[] = {128}; // Maximum shininess

Now, let’s assign these properties to lights and materials in OpenGL:

glLightfv(GL_LIGHT0, GL_SPECULAR, whiteSpecularLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, blackAmbientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, whiteDiffuseLight);

// Set materials dynamically
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecularMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mShininess);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, redDiffuseMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, greenEmissiveMaterial);

Tutorial Code

Here’s the full code for setting up lights, materials, and keyboard interactions:

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

GLfloat angle = 0.0;

GLfloat redDiffuseMaterial[] = {1.0, 0.0, 0.0}; // Red material
GLfloat whiteSpecularMaterial[] = {1.0, 1.0, 1.0}; // White material
GLfloat greenEmissiveMaterial[] = {0.0, 1.0, 0.0}; // Green material
GLfloat whiteSpecularLight[] = {1.0, 1.0, 1.0}; // White light specular
GLfloat blackAmbientLight[] = {0.0, 0.0, 0.0}; // Black ambient light
GLfloat whiteDiffuseLight[] = {1.0, 1.0, 1.0}; // White diffuse light
GLfloat blankMaterial[] = {0.0, 0.0, 0.0}; // Blank material
GLfloat mShininess[] = {128}; // Maximum shininess

bool diffuse = false;
bool emissive = false;
bool specular = false;

void init() {
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
}

void light() {
    glLightfv(GL_LIGHT0, GL_SPECULAR, whiteSpecularLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, blackAmbientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, whiteDiffuseLight);
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    light();
    glTranslatef(0, 0, -5);
    glRotatef(angle, 1, 1, 1);
    glutSolidTeapot(2);
    glutSwapBuffers();
    angle++;
}

void reshape(int w, int h) {
    if (h == 0) h = 1;
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

void keyboard(unsigned char key, int x, int y) {
    if (key == 's') {
        if (!specular) {
            specular = true;
            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecularMaterial);
            glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mShininess);
        } else {
            specular = false;
            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, blankMaterial);
            glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, blankMaterial);
        }
    }

    if (key == 'd') {
        if (!diffuse) {
            diffuse = true;
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, redDiffuseMaterial);
        } else {
            diffuse = false;
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, blankMaterial);
        }
    }

    if (key == 'e') {
        if (!emissive) {
            emissive = true;
            glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, greenEmissiveMaterial);
        } else {
            emissive = false;
            glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, blankMaterial);
        }
    }
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("A basic OpenGL Window");
    init();
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutKeyboardFunc(keyboard);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

Source & downloads

Questions or corrections? If something doesn’t work or you spot something I’ve got wrong, leave a comment below. These tutorials have been around a long time, so useful corrections are always welcome!

  • March 25, 2010
  • 5