Lesson 20: Materials

Materials

We've already seen how to light objects in OpenGL, and I'm going to explain how to use materials to specify in more detail how an object is lit.

Run and take a look at the program for this lesson. It shows a lit, spinning sphere. It has a little shininess, which is new, as shown in the below screenshot:

Materials program screenshot

You can use the 's', 'p', and 'e' keys to toogle some of the parameters in the "material" of the sphere, that is, how it's lit. Try it out. Using the 's' key, you can toggle whether the shininess is more concentrated or whether it covers a larger area. Using the 'p' key, you can toggle between a lot of shininess and almost no shininess. Using the 'e' key, you can toggle whether the shpere "emits" a grayish color, so that it is lighter and more grayish.

The Code

On to the code.

bool _highShininess = false; //Whether the shininess parameter is high
bool _lowSpecularity = false; //Whether the specularity parameter is high
bool _emission = false; //Whether the emission parameter is turned on

void handleKeypress(unsigned char key, int x, int y) {
    switch (key) {
        case 27: //Escape key
            exit(0);
            break;
        case 's':
            _highShininess = !_highShininess;
            break;
        case 'p':
            _lowSpecularity = !_lowSpecularity;
            break;
        case 'e':
            _emission = !_emission;
            break;
    }
}

We have three variables, _highShininess, _lowSpecularity, and _emission, which are toggled with the 's', 'p', and 'e' keys. These are used for some of the parameters with the material, as we'll see later.

void initRendering() {
    //...
    //Disable color materials, so that glMaterial calls work
    glDisable(GL_COLOR_MATERIAL);
}

We'll have to disable GL_COLOR_MATERIAL for the calls to glMaterialfv and glMaterialf that we'll see later to work.

void drawScene() {
    //...
    
    GLfloat ambientLight[] = {0.2f, 0.2f, 0.2f, 1.0f};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
    
    GLfloat lightColor[] = {0.6f, 0.6f, 0.6f, 1.0f};
    GLfloat lightPos[] = {1.5f * RADIUS, 2 * RADIUS, 1.5 * RADIUS, 1.0f};
    //Diffuse (non-shiny) light component
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
    //Specular (shiny) light component
    glLightfv(GL_LIGHT0, GL_SPECULAR, lightColor);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

Here, we set up the lighting. We've already seen how to set up ambient light, and we've seen calls to glLightfv with GL_DIFFUSE and GL_POSITION, but the call to glLightfv with GL_SPECULAR is new. It sets up the specular, or shiny, color and intensity for the light. We'll set it to be the same as the diffuse intensity. (Diffuse lighting is the "ordinary" lighting that we've already seen.)

    //Determine the specularity, emissivity, and shininess parameters, based on
    //variables that can be toggled with keystrokes
    float specularity;
    if (_lowSpecularity) {
        specularity = 0.3f;
    }
    else {
        specularity = 1;
    }
    
    float emissivity;
    if (_emission) {
        emissivity = 0.05f;
    }
    else {
        emissivity = 0;
    }
    
    float shininess;
    if (_highShininess) {
        shininess = 25;
    }
    else {
        shininess = 12;
    }

Now, we set some variables, based on the toggleable booleans.

    //The color of the sphere
    GLfloat materialColor[] = {0.2f, 0.2f, 1.0f, 1.0f};
    //The specular (shiny) component of the material
    GLfloat materialSpecular[] = {specularity, specularity, specularity, 1.0f};
    //The color emitted by the material
    GLfloat materialEmission[] = {emissivity, emissivity, emissivity, 1.0f};
    
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, materialColor);
    glMaterialfv(GL_FRONT, GL_SPECULAR, materialSpecular);
    glMaterialfv(GL_FRONT, GL_EMISSION, materialEmission);
    glMaterialf(GL_FRONT, GL_SHININESS, shininess); //The shininess parameter

Here, we set up the material for the sphere. First, in the materialColor array, we have the sphere's color, which is (0.2, 0.2, 1), a blue color. (As usual, the red, green, and blue components of the color are followed by a 1.) Then, materialSpecular indicates the color intensity of the specularity, or shininess, of the material. In this program, we use a white color. You could actually use another color; for example, if you change the second and third elements to be 0, the shiny portion of the sphere looks red, as shown below:

Red specularity

The specularity of the object is the product of the specularity indicated by the material and the specularity indicated by the light. So in this case, the specularity is (0.6f * specularity, 0.6f * specularity, 0.6f * specularity).

Then, we have materialEmission, an array that indicates a color intensity "emitted" by the sphere. If enabled by pressing the 'e' key, the sphere emits the color (0.05, 0.05, 0.05), so that it looks a little lighter and a little more grayish.

Having set up the arrays, we have to pass them on to OpenGL, using calls to glMaterialfv and glMaterialf. First of all, you'll notice that in our calls to glMaterialfv and glMaterialf, the first parameter is always GL_FRONT. This tells OpenGL that we want to set the material lighting for polygons that are facing the light, that is, polygons whose normals are in the opposite direction of the light. We're only setting the lighting for these polygons because we don't want the back side of the sphere to be lit. (If you wanted to set the lighting for the back polygons, you could use GL_BACK or GL_FRONT_AND_BACK instead.)

We call glMaterialfv with GL_AMBIENT_AND_DIFFUSE to set the ambient and diffuse material colors. You could set the two separately using GL_AMBIENT and GL_DIFFUSE, but it's so common to set them to the same value that OpenGL provides us with GL_AMBIENT_AND_DIFFUSE. Then, we set the specular and emmisive parameters of the material using calls to glMaterialfv with GL_SPECULAR and GL_EMMISSION respectively.

Then, we set the GL_SHININESS parameter of the sphere. If we set it to a high value, the shiny area of the sphere will be more concentrated, while if we set it to a low value, the shininess will cover a larger area. The shininess has to lie between 0 and 128.

    //Draw the sphere
    glutSolidSphere(RADIUS, 150, 80);

Now, we actually draw the sphere with a call to glutSolidSphere. For those of you who haven't seen it before, glutSolidSphere draws a bunch of polygons to resemble a sphere whose radius is the first parameter. The second and third parameters indicate the number of polygons that are used to draw the sphere.

So in this manner, using glMaterialfv and glMaterialf, we can set how an object is lit.

Next is "Part 5: A Sample Game: Crab Pong".