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 colors to objects and manipulating material properties like shininess and light reflectivity. We’ll also explore the interplay between colored 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 colored lights interact with colored 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 color 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 colors 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) {
    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;
}

If you have any questions or issues with this tutorial, feel free to email me at swiftless@gmail.com. Happy coding!

  • March 25, 2010
  • 5