Viewing


Color

Drawing on a computer screen is different from drawing o paper in that the paper starts out white, and all you have to do is draw the picture. On the computer, the memory holding the picture is usually filled with the last picture you drew, so you typically need to clear it to some background color before you start to draw the new scene. To change the background color, we call glClearColor() and specify the color we have chosen for the background. The default clearing color is (0,0,0,0) which is black. A subsequent call to glClear() will clear the specified buffers to their current clearing values. To clear the color buffer use the argument GL_COLOR_BUFFER_BIT.

To set a color, use the command glColor3f(). It takes three floating point parameters which are between 0.0 and 1.0. The parameters are, in order, the red, green, and blue components of the color. You can think of these as specifying a "mix" of colors, where 0.0 means don't use any of this color and 1.0 means use all of that component. For example, glColor3f(1.0, 0.0, 0.0) makes the brightest red that the system can draw, with no green or blue components.

All zeros makes black (an absence of colored light) and all ones makes white (a presence of all colored light). Eight commom colors and their commands are:



Shading

So far, we have applied just a single color to each polygon that we have drawn (flat shading). However, it is possible to specify a unique color at each vertex. OpenGL will smoothly interpolate the color between the vertices. This is known as Gouraud Shading (or smooth shading). OpenGL will linearly interpolate the RGB values for the pixel values along the edges between the vertices causing a gradual shift of color.


	

	    Source Code

Viewing Transformation

Recall that to change the view of an object we could either move the object or move the viewer. We will do a series of transformations on the viewer's "camera" that are similar to the transformations done on an object. To start with, we will initialize the viewing matrix by loading it with the identity matrix, using the command glLoadIdentity(), and continue to combine it with new ones according to where we want to place the "camera". The command gluLookAt is used to indicate where the viewer is placed, where it is aimed, and which way is up.

For example, gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); places the camera (or eye position) at the point (0,0,5), aims it towards the origin (0,0,0) and specifies the up-vector as (0,1,0).

At this point, we should probably mention the concept of hidden surface elimination. This is the process of making objects that are in the forefront "hide" those behind them. Recall that we have a depth buffer that keeps track of the depth of each pixel of the objects in a scene. An objects depth value is the difference between the viewpoint and the object. If you want to use the depth buffer, you simply have to enable it by passing GL_DEPTH_TEST to glEnable() and remember to clear the depth buffer before you redraw each frame by using glClearDepth(). When the buffer is enabled the pixel that is displayed takes the color of the object with the smallest z value (depth), making that object appear closer than the other object(s).

Projection

Specifying the projection transformation is like choosing a lens for a camera. You can think of this transformation as determining the field of view and therefore which objects are inside it and, to some extent, how they should look. There are two basic types of projections provided for you by OpenGL, orthographic and perspective.

Orthographic: Orthographic projection maps objects directly onto the screen without affecting their relative sizes. This projection is used mainly in architectural and computer-aided design applications, where the actual measurements of the objects are more important than how they might look. The command glFrustum() is used to set the projection transformation. The command glMatrixMode() is used, with the argument GL_PROJECTION is used to set up the matrix upon which this projection transformation (and subsequent transformations) is performed. Note that we use the glLoadIdentity() command to initialize the current projection matrix so that only the specified projection transformation(s) have an effect. Finally, we calle the command glOrtho() to create an orthographic parallel viewing volume. The viewing volume is a box with the left, right, top, bottom, near and far edges specified in the glOrtho() command.

When it is time to perform transformations on the models that we have created, we will use the same glMatrixMode() command with GL_MODELVIEW as the argument. This indicates that the succeeding transformations now affect the modelview matrix instead of the projection matrix.

Perspective: We will use the perspective projection to get a more realistic rendering of an object. The object(s) will have the unmistakable characteristic of foreshortening: the further an object is from the camera, the smaller it appears in the final image. This is because the viewing volume of perspective projection is a frustum (a truncated pyramid whose top has been cut off by a plane parallel to its base). Objects that are closer to the apex of the pyramid appear smaller while objects closer to the base appear larger. The command to define this frustrum is glFrustum(), which takes the values for left, right, top, bottom, near and far edges. You could perform rotations or translations on this projection matrix to alter its orientation, by this is tricky and should be avoided.

The same set of commands to set the projection glMatrixMode(GL_PROJECTION) and glLoadIndentity() should be used to set up the projection transformation, but instead of using the glOrtho() command the glFrustum() command is given.


This program takes the same object and creates two windows with two different views of the same set of objects.

   

                                           Source Code


Manipulating the Matrix Stacks

The modelview and projection matracies you've been creating, loading, and multiplying have been only the visible tips of their respective iceburgs. Each of these matracies is actually the topmost member of the modelview or projection matrix stacks, respectively.

Suppose you want to draw the same object at four different locations. To make life much simpiler, we can use the same object description and successively translate it to each of the desired positions.

Since the transformations are stored as matracies, a matrix stack provides an ideal mechanism for doing this sort of successive copying, translating, and throwing away. The command glPushMatrix() copies the matrix on the top of the matrix stack which was last referenced by the glMatrixMode() command. The matrix contents, therefore, are duplicated in both the top and the second-to-the-top matracies. This top matrix can then be translated and drawn, as desires, and ultimately destroyed using the glPopMatrix() command. This leaves you right where you were before and ready to repeat the process for the next version of the object.


This program will produce the same object in four different locations by manipulating the ModelView Matrix Stack.

	

	    Source Code

Light

Light effects are very important in OpenGL, for without the use of lights, an 3-D object will still look 2-dimensional. OpenGL provides two types of light sources: directional and positional. A directional light source is considered to be an infinite distance away from the objects in the scene. Thus, its rays of light are considered parallel by the time they reach the object. A positional light, in contrast, is near or within the scene and the direction of its rays are taken into account in lighting calculations.

The command glLightfv() is used to specify the position of the light, regardless of whether it is directional or positional. It is also used to specify whether the light source has ambient color, diffuse color, specular color, or emissive color.

Ambient: Light that has been scattered so much by the environment that its direction is hard to determine.
Diffuse: Light that comes from one direction, so it's brighter if it comes squarely down on a surface than it it barely glances off the surface.
Specular: Light that comes from a particular direction, and it tends to bounce off the surface in a preferred direction.
Emissive: Light seems to be orginating from an object.

You can create up to eight light sources. All of which have been internally defined as GL_LIGHT0, GL_LIGHT1, and so on. To create a light source, you must choose which light you want to use, by name, a position for the light source and certain parameters. Available parameters are color and quality.

To define the position, you must supply a vector of four values (x,y,z,w). If the last value, w, is zero, the corresponding light source is a directional one, and the (x,y,z) values describe its direction. By default, the position for GL_POSITION is (0,0,1,0), which defines a directional lighe that points along the negative z-axis.

The color components specified for lights means something different that for objects. For a light, the numbers correspond to a percentage of full intensity for each color. If the R, G, and B values for a light's color are all 1.0, the light is the brightest possible white. However, if the values are all 0.5, the color is still white, but only half the intensity, and therefore appears gray. If R=1, G=1 and B=0, the light has full red and full green with no blue, and therefore appears yellow. The forth parameter that is passed is an on-off switch, more or less. A value of 1 turns the light on, and a value of 0 turns the light off.

OpenGL allows you to associate three different color-related parameters - GL_AMBIENT, GL_DIFFUSE, and GL_SPECULAR - with any particular light. The default is no ambient light, GL_AMBIENT(0.0,0.0,0.0,1.0). The light is on, but the color is black. Similarly, the default is for a diffuse light, GL_DIFFUSE(1.0,1.0,1.0,1.0) for GL_LIGHT0, which produces a bright, white light. The default values for the other seven lights, GL_LIGHT1,..., GL_LIGHT7 is (0.0,0.0,0.0,0.0). Lights are defined as a variable before they are passed to the glLightfv() command. Finally, you need to activate the light by using glEnable(GL_LIGHTING) and glEnable(GL_LIGHT0).

For example, to create a diffuse light at position (1,1,1), you must do the following:

GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_position[] = {1.0, 1.0, 1.0, 0.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

OpenGL treats the position and direction of a light source just as it treats the position of a geometric primitive. In other words, a light source is subject to the same matrix transformations as a primitive. This means that you can change a light source's position or direction by changing the contents of the modelview matrix. (The projection matrix has no effect on a light's position or direction.) For example, to rotate a light or translate the light position so that the light moves relative to a stationary object, do the following:


	

	    Source Code

Viewport Transformation

Using our camera analogy, viewport transformation is the process of deciding on the size of our developed print. Do we want a wallet-sized photograph or a poster? By default, the viewport is to set the entire pixel rectangle of the window that is opened. You can use the glViewport() command, however, to choose a smaller drawing region. For example, you could divide the window to create a split-scene for multiple views in the same window. The command takes the x- and y-coordinate of the lower left corner of the viewport and the width and height of the viewport rectangle.


This program takes the same object and creates a window with two different viewports, the first is the default and the other is much smaller.

   

                                           Source Code



Main Menu
Back to Top
Next