CS155 Computer Graphics

OpenGL Tutorial 2

In this OpenGL tutorial we'll explore the polygon rasterization and lighting. (For a more thorough treatment of the OpenGL lighting model refer to Woo etal, Ch 5.)
  1. Create a project:
    In VC7, create a new Visual C++ Project. Select "Win32 Console Project"as the template. Create the project on your desktop with the name "MyOpenGL2." You'll be presented with an "Applications Settings" window where you should choose "Empty Project."

    Download openGL2.cpp to the project folder. Open the file from the VC7 interface and right-click to add it to the project. Build the project and run it. The application opens two windows; one is a console window and the other is an OpenGL window. The latter shows a purple sphere, though it looks more like a circle because there is no lighting in the scene. We'll add that next.

  2. Lights:
    The first step is to specify the light. Add the following code to the init() function.
    	
    
    	// enable light0 and lighting
    	glEnable(GL_LIGHT0);
    	glEnable(GL_LIGHTING);
    
    Recompile the program and run it. You should now see lighting effects but only grayscale colors. When lighting is disable, OpenGL uses the current color as specified by the glColor command to color objects. When lighting is enabled, there are two ways to specify colors. The first is just use the current color, but you have to tell OpenGL to do this. After the light is specified in init(), add the following
    	
    	glEnable(GL_COLOR_MATERIAL);
    
    Recompile and run the program. You should now see lighting effects and color!

    OpenGL supports a (system-dependent) fixed number of lights named GL_LIGHT0, GL_LIGHT1, .... , GL_LIGHTn. The default position for a light is at (0,0,1) but we can change it. Add the following to init()

    
    	// set position of light0
    	GLfloat lightPosition[]={1,1,2,1};				// light position
    	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);		// setlight position
    
    You should notice that the light has moved to the right and up.

    The function glLightv() used to is used to set various state variables with respects to the lights. The command takes three arguments. The first is the light number, which should be one of the OpenGL constants GL_LIGHTi. The second is the lighting parameter to be set, also one of the OpenGL constants. The final argument is the value the parameter should be set to. In this case, the value is a vector of floats, as specified by the fv (f for float and v for vector) in the command glLightfv().

    Note the position vector has a 4th element. This specifies the type of light; when set to 1 it specifies a point light and when set to 0 it specifies a directional light. (Don't blame me, I'm just the messenger.) If you change lightPosition[3] to 0, you will see a difference.

    GL_LIGHT0 is special in that it has a default color, which is white. In fact, OpenGL allows us to set the ambient, diffuse, and specular properties of the light separately. Add the following code to init().

    
    	// set color of light0
    	GLfloat red[] = {1,0,0,0};					// light color
    	glLightfv(GL_LIGHT0, GL_DIFFUSE, red);			// set diffuse light color
    	glLightfv(GL_LIGHT0, GL_SPECULAR, red);			// set specular light color
    
    Also change the light back to a point light (by setting lightPosition[3]=1). Now the sphere should appear red except in the back where it is only lit by ambient light. Change the code to set the light color to white:
    	// set color of light0
    	GLfloat white[] = {1,1,1,0};					// light color
    	glLightfv(GL_LIGHT0, GL_DIFFUSE, white);			// set diffuse light color
    	glLightfv(GL_LIGHT0, GL_SPECULAR, white);			// set specular light color
    
    This should look the same as when we didn't specify any color for the light, since the default for GL_LIGHT0 is white.

  3. Surface Normals
    Comment out the glutSolidSphere line, recompile your program, and run it. You should see the color of the green triangle strip change. OpenGL needs surface normals to compute lighting effects appropriately. glutSolidSphere produces a polygon mesh to approximate a sphere and it provides surface normals for each polygon in that mesh. The last normal defined for the sphere was used in lighting calculations for the triangle mesh in the last section. (Remember, OpenGL is a state machine!) With the sphere code eliminated, there is no normal defined when OpenGL draws the triangle strip. Depending on the implementation it may use some default, or not. You should always specifies normals for polygons and they should be unit vectors!

    Add the following just before the triangle strip is drawn.

     	glNormal3f(0,1,0);
    

    This illustrates a common problem that can arise in OpenGL programs, inconsistent behavior. (In this case a change in the color of the triangle strip when the sphere code is removed.) This results when OpenGL is using some state variable that you have not explicitly specified. (In this case, the normal.) Many OpenGL state variables have good defaults, but some, .e.g. normals, have no obvious right value. So always set your normals!

  4. Polygon Rasterization
    When OpenGL draws a triangle it computes the color of the vertices of the triangle and uses those colors to determine the color at other points on the triangle. To illustrate this, add the following code after the triangle strip in your program.
    	// draw Triangle 
    	float red[]={1,0,0,0};
    	float blue[]={0,0,1,0};
    	glNormal3f(0,0,1);
    	glBegin(GL_TRIANGLES);
    	glColor4fv(green);
    	glVertex3f(-5.0,0,-20.0);
    	glColor4fv(red);
    	glVertex3f(5.0,0,-20.0);
    	glColor4fv(blue);
    	glVertex3f(0.0,2.0,-20.0);
    	glEnd();
    
    
    The color at any point on the triangle is interpolated based on the distance of that point to each of the vertices. Interpolation is the default approach to coloring geometric primitives. Add the line
    	glShadeModel(GL_FLAT);
    
    before the new triangle. In flat shading mode no interpolation is performed. There are rules about which vertex color is used to color a primitive in flat shading mode, but you will typically use smooth shading. To avoid problems it is best to explicitly define the shading model. Remove the line you entered above and add the following to the init function:
    	//rasterization properties
    	glShadeModel(GL_SMOOTH);
    
    (Note, we put this in init() because this is what we want as the default mode.)

  5. Material properties
    OpenGL supports the same material properties we used in the raytracer. Uncomment the glutSolidSphere line in display() and comment the glEnable(GL_COLOR_MATERIALS) in init(0). Add the following before the sphere code.
    	
    	GLfloat white[] = {1,1,1,0};			// white
    	GLfloat purple[] = {1,0,1,0};			// purple
    	
    	glMaterialfv(GL_FRONT, GL_DIFFUSE, purple);
    	glMaterialfv(GL_FRONT, GL_SPECULAR, white);
    	glMateriali(GL_FRONT,GL_SHININESS,50);	
    
    
    You should now see a shiny purple sphere (with white highlights).


  6. Attenuation
    Next lets add some attenuation for our light. Add the following to your init function:
    	
    	float constant=1.0;						// constant attenuation
    	float linear=0.5;						// linear attenuation
    	float quadratic=0.0;						// quadratic attenuation
    	glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, constant);		// set constant attenuation term
    	glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, linear);		// set linear attenuation term
    	glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, quadratic);		// set quadratic attenuation term
    
    Note that we are using glLightf, where the f signifies a float argument.
    Recompile and see what happens. Adjust the attenuation parameter to get some nice fall off on the sphere.


  7. Lighting Transforms
    Light positions can be transformed in the same way as vertices. Add the following lines after the translate transform in display.
    	GLfloat lightPosition[]={1,1,-8,1};			// light position
    	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);	// setlight position
    
    The light position is now transformed by the translate command. Recompile and rerun your program. You should see a big change in the lighting. You may think that OpenGL realizes that the sphere is occluding the light. Unfortunately, OpenGL is not that smart -- in fact shadows are quite difficult to achieve in OpenGL (we'll talk more about that in a future tutorial). We'll explore what is going on here next but remove the two lines we happened in the next section but remove the two lines you added above before going on.

  8. Robot ground:
    Next create a "ground" for your robot world. You should use triangle strips and an overhead point light. We want to see nice illumination under the light but it should fall off towards the edges of the floor. Hint1: the color computations are done at the vertices. Hint2: the diffuse color depends on the angle between the direction of the light and surface normal, just as in the raytracer.


Updated Oct. 2011