Lighting and Shading

To render shaded objects (either flat- or smooth-shaded), there are two things to worry about:

  • Lights must be placed around the scene.
  • Objects must be assigned surface normals and materials.

So far you know about normals, but lighting models will not be covered in lecture until later.

The goal in this lab, then, is not to teach you the theory of lighting, but only to impart the necessary programming details to get lighting working for the third assignment.

1. Lighting

First of all, let's look at the lighting side. Lighting parameters are set with the function glLight**() (where ** can be one of f, i, fv, or iv).

  • The first argument is a light ID: GL_LIGHTn, where 0 <= n < GL_MAX_LIGHTS (usually 8).
  • The second argument is the parameter, such as GL_POSITION for setting the location in space, or GL_AMBIENT, GL_DIFFUSE, and GL_SPECULAR for setting the color.
  • The third argument specifies the parameter value.

For example, to place light 0 at location <1, 1, 0> with default color values you might use the following code (during initialization):

 // Set light parameters
 GLfloat light0_pos[] = {1, 1, 0, 1}; // Fourth value = 1 -> positional light
 glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);

 // Enable lights
 glEnable(GL_LIGHTING);
 glEnable(GL_LIGHT0);

Try putting this code into your initialization function (eg. InitGL()). Then you can test is out by rendering a sphere with glutSolidSphere() (which generates normals automatically and will therefore light properly). You should see something like this:

(If it looks weird, make sure you are calling glShadeModel(GL_SMOOTH) and glEnable(GL_DEPTH_TEST) in your initialization.)

We can change the color of the light source by modifying the ambient, diffuse, and specular parameters. For now, don't worry about what each of these things are. Just think of the diffuse component as the light source's color. To specify a reddish light, we might add the following lines.

 GLfloat light0_dif[] = {0.8, 0.2, 0.2, 1.0};
 glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_dif);

Now you'll see something like this when you run the program.

2. Shading

Okay, now that we've got some lights in the scene, we need to know how to change the appearance of objects in the scene. There are two main components: specifying normals, and specifying materials.

2.1 Normals

By now you should know what a normal vector is and how to compute them for a polygon mesh. To specify the normal when rendering an object, you use the glNormal**() function, which works exactly like glVertex**, i.e. you pass it the nD coordinates of the normal vector.

For smooth shading, each vertex needs to have a normal assigned to it. To assign a normal N to vertex v, you would do this:

 glBegin(GL_POLYGON);
 	...
 	glNormal3fv(N);
 	glVertex3fv(v);
 	...
 glEnd();

For flat shading, the idea is to assign a normal to each polygon face. This can happen in two ways:

  • First: if you change the shading model to glShadeModel(GL_FLAT), then OpenGL will determine the color of a polygon from the color of the first vertex. Consider the following code snippet
 glBegin(GL_POLYGON);
 glNormal3fv(N1); // Set normal 
 glVertex3fv(v1); // N1 used to determine color
 glNormal3fv(N2); // has no effect
 glVertex3fv(v2); // same color as v1
 ...
 glEnd();
  • Second: if you keep the shading model as GL_SMOOTH, then you can achieve flat-shading by assigning the same normal to each vertex in a polygon. Consider the following code snippet:
 glNormal3fv(FN1);    // Set face normal 
 glBegin(GL_POLYGON); // Draw the face
 glVertex3fv(v1); 
 ...
 glEnd();

2.2. Materials

The next part of shading is to set the surface material properties. Material properties determine how light interacts with an object in terms of reflectance. What color is the object? Is the object shiny or dull? Does the object emit light itself?

We'll ignore the latter two questions for now, and focus on the issue of object color. Much like a light source, an object has ambient, diffuse, and specular material properties. Specularity relates to shininess, while ambient and diffuse relate to the "native" color. All of these properties are set by calls to glMaterial**().

For example, taking the properties of Gold from the above page, the following code snippet specifies a gold material.

 GLfloat amb[] = {0.329412, 0.223529, 0.027451, 1.0};
 GLfloat dif[] = {0.780392, 0.568627, 0.113725, 1.0};
 GLfloat spe[] = {0.992157, 0.941176, 0.807843, 1.0};
 GLfloat shi[] = {27.8974};

 glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
 glMaterialfv(GL_FRONT, GL_DIFFUSE, dif);
 glMaterialfv(GL_FRONT, GL_SPECULAR, spe);
 glMaterialfv(GL_FRONT, GL_SHININESS, shi);

If you put this code before the sphere is drawn, you'll see the output change to this:

As you can see, changing materials is a bit of a pain in the butt, requiring several OpenGL calls. The internal overhead of changing material is also high, so you don't want to do it all the time. For many applications, in fact, using materials at all may be overkill. For all these reasons, OpenGL provides another interface to material specification based on calls to glColor().

First of all, let's try to just use glColor() to set the color of the sphere. Try using these lines in your drawing function (with lighting set up and enabled):

 glColor3f(0, 0, 1); 
 glutSolidSphere(2, 16, 16);

Ideally, we'd see a blue sphere. Unfortunately, we don't. When lighting is turned on, the color of an object is determined by the material properties and not glColor().

What we'd like, then, is a way to have glColor() map to material properties. This is exactly what GL_COLOR_MATERIAL does -- it ties calls to glColor() to corresponding calls to glMaterialfv(). Consider the following code snippet:

 // In initialization somewhere...
 glEnable(GL_COLOR_MATERIAL);
 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

 // In draw function
 glColor3f(r, g, b);
 ...

This is equivalent to

 // In draw function
 GLfloat color[] = {r, g, b, 1};
 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);

So, if you enable GL_COLOR_MATERIAL as above, then you can alter the object color with glColor() as usual.

After enabling GL_COLOR_MATERIAL, the blue sphere is indeed blue:

You're free to use either "proper" materials or color_materials for your assignment.