# CS315 Lab 4: Illumination

## Highlights of this lab:

This lab is an introduction to illumination
• Overview of colors, materials and lights
• A color cube example "COLOR"
• Lighting examples "LIGHTING 1", "LIGHTING 2", and "LIGHTING 3"

For examples mentioned in the lab notes click here (Windows only for now)

## Assignment:

After the lab lecture, you have approximately one week to:
• make some modifications to "smooth.cpp"
• make some modifications to "movelight.cpp"
• show instructor results and answers to questions
Online OpenGL Manual

## Lab Notes

### A. Colors

#### Color Modes

OpenGL supports two color modes. The first is RGBA model, where you select the value of the red, green, blue and alpha(opacity) parameters. The second is color-index mode, whereby you fill a palette with the colors. We will focus on RGBA mode, because most of the interesting things you can do involve lighting and textures, which are difficult to do using a color palette. When you enable lighting, the color of the vertex is determined by the material color assigned to the vertex, the color and position of the light shining on the vertex, and the viewer's position relative to the vertex. When you disable lighting, the color of a vertex is simply the effect of the color setting.

Color in RGBA mode is set by specifying the red, green, blue and alpha intensities. The glColor*() comes in a variety of formats. You can specify a bright red triangle using the following commands:

```glColor3f(1.0f, 0.0f, 0.0f);  //no alpha value form
glBegin(GL_TRIANGLES);
glVertex3f(-1, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(0, 1, 0);
glEnd();
```

Notice that the glColor*() function can be placed inside a glBegin()/glEnd() pair. Thus you can specify individual colors for each individual vertex if you desire. If the colors of two vertices are different, what is the color between the two vertices? For example, what is the color of the center of the triangle in the following code?

```glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);  //red
glVertex3f(-1, 0, 0);
glColor3f(0.0f, 1.0f, 0.0f);  //green
glVertex3f(1, 0, 0);
glColor3f(0.0f, 0.0f, 1.0f);  //blue
glVertex3f(0, 1, 0);
glEnd();
```

The answer is that it depends on the shading model specified. If smooth shading(default) is specified, the color values are interpolated between vertices. In this case the color at the center would be gray. If flat shading is specified, the one vertex is selected as being representative of all the vertices; thus the entire primitive is displayed using one single color. For a single polygon, the first vertex is used to specify to color; for all the other primitives it is the last specified vertex in each polygon or line segment.

The following is a cube model with the top left off and a small black ball is placed inside the cube.

ColorView.cpp
```
glColor3f( 0.0f, 0.0f, 0.0f );
glutSolidSphere( 0.3f ); // slight memory leak here if you didn't initalize GLUT
// (using MFC or Cocoa...)

// define the colors
GLfloat color1[3] = { 1.0f, 0.0f, 0.0f }; // red
GLfloat color2[3] = { 0.0f, 1.0f, 0.0f }; // green
GLfloat color3[3] = { 0.0f, 0.0f, 1.0f }; // blue
GLfloat color4[3] = { 1.0f, 1.0f, 1.0f }; // white
GLfloat color5[3] = { 0.0f, 0.0f, 0.0f }; // black
GLfloat color6[3] = { 1.0f, 0.0f, 1.0f }; // magenta
GLfloat color7[3] = { 0.0f, 1.0f, 1.0f }; // cyan
GLfloat color8[3] = { 1.0f, 1.0f, 0.0f }; // yellow

// Connect the four sides
glColor3fv( color6 );
glVertex3f(-1.0f, 1.0f, 1.0f);

glColor3fv( color1 );
glVertex3f(-1.0f, -1.0f, 1.0f);

glColor3fv( color4 );
glVertex3f(1.0f, 1.0f, 1.0f);

glColor3fv( color8 );
glVertex3f(1.0f, -1.0f, 1.0f);

glColor3fv( color7 );
glVertex3f(1.0f, 1.0f, -1.0f);

glColor3fv( color2 );
glVertex3f(1.0f, -1.0f, -1.0f);

glColor3fv( color3 );
glVertex3f(-1.0f, 1.0f, -1.0f);

glColor3fv( color5 );
glVertex3f(-1.0f, -1.0f,  -1.0f);

glColor3fv( color6 );
glVertex3f(-1.0f, 1.0f, 1.0f);

glColor3fv( color1 );
glVertex3f(-1.0f, -1.0f, 1.0f);

glEnd();

// The Bottom

glColor3fv( color1 );
glVertex3f(-1.0f, -1.0f, 1.0f);

glColor3fv( color8 );
glVertex3f(1.0f, -1.0f, 1.0f);

glColor3fv( color2 );
glVertex3f(1.0f, -1.0f, -1.0f);

glColor3fv( color5 );
glVertex3f(-1.0f, -1.0f,  -1.0f);
glEnd();
```

Figure 1: The color cube with smooth shading selected

This model draws the color cube -- red, green and blue as one triplet; cyan, magenta and yellow as another triplet; Figure 1 shows the effect of smooth shading. With no lighting effects, it is very hard to distinguish between the faces of the polygons that makes up an object. Figure 2 shows the same scene with flat shading. The color of each face is entirely the result of the order in which the vertices were specified.

Figure 2: The color cube with flat shading selected

### B. Materials

OpenGL's materials are descriptions of what things are made of -- or at least what they appear to be made of -- by describing how they reflect light.

#### Types of material properties:

OpenGL has five properties that describe how a surface dissipates light. These properties are typically described by RGBA values that stipulate the color of the dissipated light.

Diffuse and ambient properties:
The diffuse and ambient reflective material properties are a type of reflective effect that is independent of the viewpoint. Diffuse lighting describes how an object reflects a light that is shining on the object. That is, it is how the surface diffuses a direct light source. Ambient lighting describes how a surface reflects the ambient light available. The ambient light is the indirect lighting that is in a scene: the amount of light that is coming from all directions so that all surfaces are equally illuminated by it.
Specular and shininess properties:
The specular and the shininess properties of the surface describe the reflective effects that are affected by the position of the viewpoint. Specular light is reflected light from a surface that produces the reflective highlights in a surface. The shininess is a value that describes how focused the reflective properties are.
Emissive property:
Emissive light is the light that an object gives off by itself. A light source is typically the only object that you may give an emissive value. Lamps, fires, and lightning are all objects that give off their own light.

#### Specifying a Material Property:

Specifying a material property is almost the same as specifying a color. Let's specify a gray material for both the ambient and diffuse properties.

```
GLfloat materialDiffuse[] = {0.2, 0.2, 0.2, 1};
GLfloat materialAmbient[] = {0.5, 0.5, 0.5, 1};
glMaterialfv(GL_FRONT, GL_DIFFUSE, materialDiffuse);
glMaterialfv(GL_FRONT, GL_AMBIENT, materialAmbient);
```

glMaterial*() is used to specify the materials properties that make up the resultant color of a surface. The first parameter indicates which face of a polygon the property should be applied to.

Please refer to glMaterial*( ) Description.

#### How To Choose the Material Properties of an Object?

The steps are as follows:

1. Decide on the diffuse and ambient colors;
2. If the object has a hidden interior, maybe only the front faces are needed to be included in the calculations, thus the GL_FRONT is applied.
3. Decide on the shininess of the object.
4. Decide whether the object is giving off light. If it is, assign it the emissive properties

#### Creating material properties

In the LIGHTING 1 example, the program creates a 4X4X4 matrix of blue spheres and varies the ambient and diffuse properties along the y-axis, the specular along the x-axis, and the shininess along the z-axis. The values are as follows:

```// set up arrays of various properties
// placed here for convenience
GLfloat materialSpecular[4][4] = {
{ 0.1f,  0.1f,  0.1f, 1.0f },
{ 0.33f, 0.33f, 0.33f, 1.0f },
{ 0.67f, 0.67f, 0.67f, 1.0f },
{ 0.9f,  0.9f,  0.9f,  1.0f },
};

GLfloat materialAmbDiff[4][4] ={
{ 0.0f, 0.0f, 0.12f, 1.0f },
{ 0.0f, 0.0f, 0.25f, 1.0f },
{ 0.0f, 0.0f, 0.50f, 1.0f },
{ 0.0f, 0.0f, 1.00f, 1.0f },
};

GLfloat materialShininess[4][1] = {
{ 0.0f },
{ 5.0f },
{ 25.0f },
{ 125.0f }
};
```

These values were chosen because they give a relatively even increase in each property as the values change. Figure 3 presents the changes that these properties bring when combined. The color of the spheres are defined by the ambient and diffuse values, and you can see from both the array and the illustration that the colors start off as a very dark blue rise to a bright pure blue. The shininess effect can be observed as the highlight goes from being spread across the entire illuminated hemisphere to a point of light on the surface. If you rotate the matrix about the y-axis, you will see that the highlight disappears when the angles of the reflection no longer hits the viewpoint.

Figure 3: A matrix of spheres showing the range of material properties

#### Simpified Materials

You can simplify the specification of material properties by enabling GL_COLOR_MATERIAL. This mode uses glColor*() calls to set one or more material properties. It is often easier and faster to use color material mode to switch properties than to specify the whole material.

By default the properties modified by glColor*() calls in color material mode are diffuse and ambient. These can be changed by using glColorMaterial().

Please refer to the glColorMaterial() description.

### C. Lighting

OpenGL has two types of lighting: global, or ambient, light with its associated parameters, and individual light sources, each with their own set of parameters.

#### Enabling Lighting

We use glEnable(GL_LIGHTING) to turn on lighting calculations, and the default values for the global ambient light will let you see the objects in the scene. In addition to the global ambient light, you can turn on at least eight individual lights. The actual number depends on the OpenGL implementation that's running. You enable each individual light by using a GL_LIGHT* argument to glEnable(), where * is a number from 0 to 7 (or more if your hardware supports it). Default values for GL_LIGHT0 let its effect be visible; the other lights have default values set so that they are disabled and do not enter into the lighting calculations.

#### Global Lighting

The glLightModel*() function is used for setting global lighting parameters. You can select the RGBA value for the global ambient light that's present in the scene. Next, you can alter the calculations for specular highlights:

• Is the viewer local? (false by default)
• Should I calculate lighting for both front and back faces? (false by default)

#### Individual Light Sources

An individual light source will emphasize the differences between differently illuminated sides. After enabling an individual light source, you may want to give the light a color, position or direction, or set other useful parameters. A light's parameters are set with the glLight*() function. The parameters passed in are the particular light's enum value, the enum of the parameter you want to change, and the value of the parameter.

This function can be used to do the following:

##### Set the Light "Color"

The diffuse RGBA value of the color that the light contributes to the reflectance off an object and is what you can consider the "color" of the light. Shining a light with red diffuse RGBA settings on a white sphere would give a red coloring to that part of the sphere that the light illuminates. The specular component is the color of the highlights that reflect off a shiny surface. Typically you would set the specular value to be the same as the diffuse value, since the reflected highlight is usually the same as light. Figure 4 is a scene from the "LIGHTING 2" program. These are three white mildly reflective spheres. The sphere in the middle has a light with white diffuse and specular shining on it. The sphere on the left has a green diffuse and white specular light shining on it. The sphere on the right has a white diffuse and a green specular lighting on it.

Figure 4: Three identical spheres lit by different light components

##### Set the Light Position

The position is transformed by the modelview matrix when glLight*( ) is called (just as if it were a point), and it is stored in eye coordinates. If the w component - the fourth value - of the position is 0.0, the light is treated as a directional source. Diffuse and specular lighting calculations take the light's direction, but not its actual position, into account, and attenuation is disabled. Otherwise, it is positional source. Diffuse and specular lighting calculations are based on the actual location of the light in eye coordinates, and attenuation is then enabled. The default position is (0,0,1,0); thus, the default light source is directional, parallel to, and in the direction of the -z axis.

##### Set the Light's Attenuation:

Attenuation is the reduction in the intensity of the light as the distance increases. OpenGL has three parameters to calculate the attenuation factor. GL_CONSTANT_ATTENUATION is the constant, and by default its value is 1. The other parameters -- GL_LINEAR_ATTENUATION and GL_QUADRATIC_ATTENUATION -- are used in the calculation to reduce the light

Consult the documentation page for glLightf or glLighti functions to see how to set the attenuation parameters.

##### Create a Spotlight
The range of light dispersion with GL_SPOT_CUTOFF parameter specifies the angle from the centerline of the spotlight's direction. In figure 5, A value 15 degrees defines cone of 30 degrees wide. By default this is 180 degrees, yielding a full 360 degrees. The acceptable range is [0,90], except for the special value of 180. To set the direction of the spotlight, use the GL_SPOT_DIRECTION parameter. The vector is in object coordinates, and it's effected by whatever matrix transformations are effective. By default it's pointing down the negative z-axis. The final setting for spotlights controls how intense the light is at its center. The GL_SPOT_EXPONENT controls how to concentrate the light's intensity. By default the light is equally bright across the diameter of the spotlight. The exponent value of the power the exponent on a cosine of the angle from the center of the spot. The higher the value, the more concentrated the light will be at the center.

Figure 5: Components of a spotlight

#### Creating a scene with multiple light sources

The following example uses multiple lights to create a scene. The source code for this can be found in the "LIGHTING 3" subdirectory. Figure 6 shows a rendering of the scene.

Figure 6: 3D text demonstrating chrome text with colored lights

The following is the RenderScene( ) member function:

LightingView.cpp
```
BOOL CLightingView::RenderScene( void )
{

// Need to add a check for the font here before assuming it's there...
static GLuint myFont = GenerateDisplayListForFont( "Impact", 0.2f );

GLfloat materialSpecular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat materialShininess[1] = { 128.0f };
GLfloat materialAmbient[4] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat materialDiffuse[4] = { 0.4f, 0.4f, 0.4f, 1.0f };

GLfloat local_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };

::glEnable(GL_DEPTH_TEST);
::glDepthFunc(GL_LESS);

::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, local_ambient);

GLfloat ambient0[] =  { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat diffuse0[] =  { 1.0f, 0.0f, 0.0f, 1.0f };
GLfloat specular0[] = { 1.0f, 0.0f, 0.0f, 1.0f };
GLfloat position0[] = { 2.0f, -1.5f, -1.5f, 1.0f };

GLfloat ambient1[] =  { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat diffuse1[] =  { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat specular1[] = { 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat position1[] = { 2.0f, 1.0f, -1.0f, 0.0f };

GLfloat ambient2[] =  { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat diffuse2[] =  { 0.0f, 0.0f, 1.0f, 1.0f };
GLfloat specular2[] = { 0.0f, 0.0f, 1.0f, 1.0f };
GLfloat position2[] = { -0.5f, -0.5f, -1.0f, 1.0f };

GLfloat ambient3[] =  { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat diffuse3[] =  { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat specular3[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat position3[] = { 2.0f, 0.5f, 0.5f, 0.0f };

// Disable lighting to draw some simple spheres that
// represent the lights
::glDisable( GL_LIGHTING );

::glPushMatrix();
::glColor3fv( diffuse0 );
::glTranslatef( position0[0], position0[1], position0[2] );
::auxWireSphere( 0.15f );

::glPopMatrix();

::glPushMatrix();
::glColor3fv( diffuse1 );
::glTranslatef( position1[0], position1[1], position1[2] );
//      ::auxWireSphere( 0.15f );
::glPopMatrix();

::glPushMatrix();
::glColor3fv( diffuse2 );
::glTranslatef( position2[0], position2[1], position2[2] );
::auxWireSphere( 0.15f );

::glPopMatrix();

::glPushMatrix();
::glColor3fv( diffuse3 );
::glTranslatef( position3[0], position3[1], position3[2] );
//      ::auxWireSphere( 0.15f );
::glPopMatrix();

::glEnable( GL_LIGHTING );

::glEnable(GL_LIGHT0);
::glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0);
::glLightfv(GL_LIGHT0, GL_POSITION, position0);
::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0);
::glLightfv(GL_LIGHT0, GL_SPECULAR, specular0);

::glEnable(GL_LIGHT1);
::glLightfv(GL_LIGHT1, GL_AMBIENT, ambient1);
::glLightfv(GL_LIGHT1, GL_POSITION, position1);
::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1);
::glLightfv(GL_LIGHT1, GL_SPECULAR, specular1);

::glEnable(GL_LIGHT2);
::glLightfv(GL_LIGHT2, GL_AMBIENT, ambient2);
::glLightfv(GL_LIGHT2, GL_POSITION, position2);
::glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse2);
::glLightfv(GL_LIGHT2, GL_SPECULAR, specular2);

::glEnable(GL_LIGHT3);
::glLightfv(GL_LIGHT3, GL_AMBIENT, ambient3);
::glLightfv(GL_LIGHT3, GL_POSITION, position3);
::glLightfv(GL_LIGHT3, GL_DIFFUSE, diffuse3);
::glLightfv(GL_LIGHT3, GL_SPECULAR, specular3);

::glMaterialfv( GL_FRONT, GL_SPECULAR, materialSpecular );
::glMaterialfv( GL_FRONT, GL_SHININESS, materialShininess );
::glMaterialfv( GL_FRONT, GL_DIFFUSE, materialDiffuse );
::glMaterialfv( GL_FRONT, GL_AMBIENT, materialAmbient );

::glPushMatrix();
::glTranslatef( -3.0f, 0.0f, 0.0f );
GLTextOut( myFont, "OpenGL & Windows" );
::glPopMatrix();

return TRUE;
}
```

## Assignment

This exercise is broken into two parts:

1. Color
2. Light and Material

### Part 1-Color

Goals:

• Learn some different calls to glColor

Instructions
Record answers for 4, 5, and 6

1. Create a GLUT project
2. Add the following code to the project: smooth.cpp
3. Build and Run this code
4. Change the glShadeModel from GL_SMOOTH to GL_FLAT.
What color is the triangle? Why?
5. Change the GL_TRIANGLES to GL_LINE_LOOP.
What color are the lines? Why?
6. Change the GL_LINE_LOOP to GL_POLYGON.
What color is the triangle now? Why?
7. Undo steps 4, 5, and 6.
8. Replace the glColor3f calls that color the triangle with three different versions of glColor to create the full intensity (red, green, blue) of the original triangle. The following table outlines the suffixes you can use and provides a hint of the maximum values (See also Table 5-1 in the Redbook).
```    Suffix   Data Type
b        1-byte integer
s        2-byte integer
i        4-byte integer
ub       unsigned 1-byte integer
us       unsigned 2-byte integer
ui       unsigned 4-byte integer
```

/4

### Part 2-Light and Material

Goals:
• Learn about positional versus directional light sources
• Experiment with attenuation parameters
• Learn about global ambient light
Instructions
Record answers for 2, 4, 6, and 11.
Make comments in the code where you make changes.
1. Use the following code as the basis fo this exercise:
movelight.cpp
2. This code has a positional light which rotates around a Torus.
From the code, how would you determine that the light is positional?
3. Left mouse click to see how the light rotates around the object.
4. Create a "direction" array similar to the "position" array that specifies a directional light and use it.
What happens? (HINT: move the light around the torus)
5. Change the light back to a positional light. Please leave the "direction" array and the call that uses it in your code with comments.
6. You will notice that there is a glShadeModel(GL_SMOOTH); call.
What effect does this have on the torus's appearance? Is this desirable? What is the alternative?
7. Replace the rotation with translation. Clicking on the right mouse button should move the light toward the right, and clicking the left mouse click should move the light toward the left. Allow for unlimited movement left and right.
8. Change the light's attenuation so that the light decreases in intensity as it's moved away from the object.
9. Try to change the color of the Torus to red using glColor. Does it work?
10. Use glMaterialfv with diffuse and ambient values set to pure red.
11. Use glLightfv with diffuse and ambient values set to pure green. What would you expect to happen? Why? What actually happens?
12. Even though you may have predicted otherwise, the object is viewable. This is because there is a global ambient light on the scene, which is set to (0.2,0.2,0.2,1.0).
13. Turn off global ambient light by using the following command:
`glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient)`
lmodel_ambient should be an array with RGBA values.
14. Your final submission should not show the torus - it should be black. Place comments near your changes to the code. Make sure your have written answers to 2, 4, 6 and 11.

/6

### Deliverables

• Code for part 1 with appropriate comments.
• Written answers to 4,5, and 6.
• Code for part 2 with appropriate comments.
• Written answers to 2, 4, 6, and 11.