GRAPHICS PROGRAMMING WITH OPENGL
© 1997

A supplement for

COMPUTER GRAPHICS, Second Edition
Hearn & Baker

Prentice Hall 1997

OPENGL SYNTAX AND PROGRAM STRUCTURE

BASIC OPENGL FUNCTIONS, CONSTANTS, AND DATA TYPES

Function names in the basic OpenGL library begin with the letters gl, and each component word within a name has its first letter capitalized. The following examples illustrate this naming convention: glClear (), glCopyPixels () and glPolygonMode ().

Certain functions require arguments that are constant values specifying a parameter name, a value for a parameter, a particular mode, etc. All such constants begin with the letters GL, component words in the constant are written in capital letters, and the underscore (_) is used as a separator between the words. A few examples of the several hundred constants available in OpenGL are GL_CCW, GL_RGB, and GL_AMBIENT_AND_DIFFUSE.

A variety of data types for ANSI C implementations can be used in OpenGL functions. Some examples are GLfloat, GLubyte, GLbitfield, and GLvoid.

Some arguments of OpenGL functions can be assigned values using a vector that lists a set of data values. This is an option for specifying a list of values as a pointer to an array, rather than specifying each element of the list explicitly as a parameter argument. A typical example of the use of this option is in specifying xyz coordinate values.

RELATED LIBRARIES AND ROUTINES

In addition to the basic, or core, library of functions, a set of "macro" routines that use core functions are available in the OpenGL Utility Library (GLU). These routines provide methods for setting up viewing projection matrices, describing complex objects with line and polygon approximations, surface rendering, and other complex tasks. In particular, GLU provides methods for displaying quadrics using linear-equation approximations.

There are a number of window-system libraries that support OpenGL functions. The OpenGL Extension to the X Window System (GLX) provides a set of routines that are prefixed with the letters glX. The WGL routines provide a Microsoft Windows-to-OpenGL interface. These routines are prefixed with the letters wgl. The PGL routines, which are prefixed with pgl, provide a Presentation Manager to OpenGL interface for the IBM OS/2. And the GLUT (OpenGL Utility Toolkit) routines, prefixed with glut, provide a toolkit that is independent of any window system. This set of routines also contains methods for describing and rendering quadric curves and surfaces.

An object-oriented toolkit based on OpenGL, called Open Inventor, provides routines and predefined object shapes for interactive three-dimensional applications. This toolkit is written in C++.

HEADER FILES

#include <GL/gl.h> Includes the OpenGL core header file. This file is required by all OpenGL applications.
#include <GL/glu.h> Includes the OpenGL Utility Library header file. This file is needed by most OpenGL applications.

And we would also need a window interface library. Alternatively, we can just include the GLUT header file:

#include <GL/glut.h> Includes the OpenGL Utility Toolkit header file. This statement automatically includes gl.h, glu.h, and glx.h. And with Microsoft Windows, it includes the appropriate header file to access WGL.

SETTING UP DISPLAY WINDOWS USING GLUT

glutInit (options); Initializes GLUT and specifies command-line options for the window system in use. This function should be called before any other GLUT routine.
glutInitWindowPosition (x, y); Specifies the screen position for one corner of the window.
glutInitWindowSize (w, h); Specifies the width (w) and height (h) for the window in pixels (integer values).
glutCreateWindow (string); Opens a window with the previously specified size, position, and other properties. The specified text string may be displayed in the window title bar, depending on the options available in the window system.

Various options for the window are set with the glutInitDisplayMode function. These options include the color mode and single or double buffering. The default is RGB color mode with single buffering.

SETTING THE WINDOW COLOR

Background color for a window is set with the functions glClearColor and glClear. For example, the following two statements set the background color to black (the default), then this color is applied to the color buffer which displays the window with the specified color.

	glClearColor (0.0, 0.0, 0.0, 0.0); 
	glClear (GL_COLOR_BUFFER_BIT);

In the glClearColor function, the first three parameters set the RGB color components in the range from 0 to 1. The fourth parameter is used to set an "alpha value", as discussed in the section Attributes of Output Primitives, Color.

WINDOW COORDINATES AND SCREEN DISPLAY AREA

There are a number of options for setting viewing parameters and generating a screen display of a scene. The various options for generating views of two-dimensional and three-dimensional scenes are discussed in the section on Viewing Transformations.

For a two-dimensional application, we can obtain a screen display of a defined scene with the following code segment which sets up a coordinate "viewing" range and specifies an area of the screen for displaying the scene.

	glViewport (xvmin, yvmin, vpWidth, vpHeight);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity (); 
	gluOrtho2D (xwmin, xwmax, ywmin, ywmax); 

The first command above defines the rectangular screen area (called the "viewport") that is to be used for the display of the scene. All parameters in the glViewport function are assigned integer screen coordinate values, and the lower left corner of the viewport is displayed at screen position(xvmin, yvmin) in the display "window" set up by the window manager. The second command sets the mode for matrix operations. In this case, we want to project a scene description to the screen. The third command initializes the transformation matrix for these operations to the identity matrix. And the fourth and last command defines the range of coordinates that will map to the viewport, with coordinate values expressed as floating-point numbers. This fourth function defines a "clipping window", so that objects inside this rectangle are displayed within the viewport. Objects outside this rectangle are clipped; i.e., not displayed. As an alternative, the function gluOrtho2D could be replaced with glOrtho, as discussed in the section on Viewing Transformations.

DISPLAYING A SCENE USING GLUT

After initializing GLUT, setting the window parameters (position, size, and background color), and specifying the coordinate system, we can display a scene with the following functions

	glutDisplayFunc(sceneDescrip);
	glutMainLoop ();

The parameter sceneDescrip in the first function is a routine that describes the primitives and attribute settings for the scene. Rendering and display of window contents are effective with the final GLUT function: glutMainLoop.

 

OUTPUT PRIMITIVES

POINT PLOTTING

The function glVertex () specifies the coordinates for a point position.

We define world-coordinate positions with glVertex functions placed between a glBegin/glEnd pair using the primitive type constant: GL_POINTS. Coordinate positions can be specified in two or three dimensions. We can also use homogeneous-coordinate representations (four dimensional). Default values for the z coordinate and the h parameter in coordinate specifications are z = 0 and h = 1. We use a suffix (2, 3, or 4) on the glVertex to indicate the coordinate dimension.

The data type to be used in specifying a particular cordinate position is also indicated with a suffix code on the glVertex function. These suffix codes are double (d), float (f), integer (i), and short (s). Coordinate values can be explicitly listed, or they can be given in a separate array designation. For an array specification of a coordinate position, we append a third suffix code: v (for "vector").

In the following example, three points are plotted along a two-dimensional straight-line path with a slope of 2. Coordinates are given as integers.

	glBegin (GL_POINTS);
	  glVertex2i (50, 100);
	  glVertex2i (75, 150);
	  glVertex2i (100, 200); 
	glEnd ();

Alternatively, we could have used a vector specification for coordinate positions by replacing each of the statements between the glBegin/glEnd pair with a statement of the form

	glVertex2iv (endpointCoords1);

where parameter endpointCoords1 is a pointer to an array of coordinate values.

LINE FUNCTIONS

As with point plotting, straight line segments are specified with glVertex functions that are placed within glBegin/glEnd pairs. In this case however, the coordinate positions are interpreted as line endpoint positions. Straight line segments are drawn as solid lines, unless other attribute options are selected. There are three primitive types in OpenGL that we can use to generate line segments:

GL_LINES Generates a series of unconnected line segments between each successive pair of specified endpoints. Thus, we obtain one straight line segment between the first and second coordinate points, then another line segment between the third and fourth points, and so forth up to the final pair of endpoint positions. If the number of specified endpoints is odd, the last endpoint position is ignored.
GL_LINE_STRIP Generates a "polyline" of connected line segments between the first endpoint and the last endpoint.
GL_LINE_LOOP Generates a series of connected line segments the same as GL_LINE_STRIP, but then adds a final line segment from the last point back to the first point specified.

Example:

	glBegin (lineMode); 
	  glVertex2i (50,150);
	  glVertex2i (150, 150);
	  glVertex2i (150, 50);
	  glVertex2i (50, 50);
	glEnd (); 

If parameter lineMode in this example is set to the value GL_LINES, we obtain two unconnected line segments that are horizontal and parallel. With GL_LINE_STRIP, we have a connected polyline with three line segments between position (50, 150) and position (50, 50). And with GL_LINE_LOOP, we draw the four edges of a square, where each edge is 100 pixels long.

DISPLAYING CURVED LINES

Since curves cannot be precisely described with linear equations, there are no simple curve functions, such as those for points and straight lines, in OpenGL. One way to display a curve is to approximate it with discrete points or straight-line segments. For example, we could set up routines to implement midpoint methods for any given function, although this is generally not straightforward for complex curves. Another approach is to generate curves using the spline methods that are available in OpenGL. For quadrics, such as circles and ellipses, there are routines available in the OpenGL Utility Library (GLU).

FILL-AREA FUNCTIONS

We display filled polygons with the same procedures that we used for straight-line drawing, except that the glVertex function is now used to give the polygon vertex coordinates. Different shapes are obtained by specifying different polygon type constants in the glBegin command. By default, polygons are displayed with a solid fill color. Other attribute options, such as pattern fill and border display, are also available. There are six polygon fill primitives available in OpenGL:

GL_POLYGON Generates a single polygon fill area from a list of vertex coordinate positions. The fill area is inside the boundary formed by the straight-line edge segments that connect vertex positions. The specified vertex list must contain three or more positions for anything to be displayed, the vertices must form a convex polygon, and there must be no crossing edges.
GL_TRIANGLES Generates a set of unconnected triangle fill areas. The first triangle is formed with the first three vertices specified, the next triangle is formed from the next three vertices, and so on. If the number of vertices specified is not a multiple of 3, the final one or two vertex positions are not used.
GL_TRIANGLE_STRIP Generates a set of connected triangles. Each successive triangle shares an edge with the previously specified triangle. Thus, the ordering of the vertex list must be set up to ensure a consistent display. For N specified vertex positions, we obtain N-2 triangles in the strip. If N is odd, vertices n, n+1, and n+2 are used to form triangle n. If n is even, vertices n+1, n, and n+2 are used to form triangle n, where n = 1, 2, ... , N-2. This is required so that the triangles can form a consistent set of facets for a single surface. The vertex list should contain at least three coordinate positions.
GL_TRIANGLE_FAN Generates a set of connected triangles that all share a common vertex. For N specified vertices, we obtain N-2 triangles in the fan. The first vertex along with vertices n+1 and n+2 are used to form triangle n, where n = 1, 2, ... , N-2. Thus, the first triangle is specified with three vertices, and each additional vertex defines the next connecting triangle.
GL_QUADS Generates a set of unconnected quadrilaterals (four-sided polygons). This primitive is similar to GL_TRIANGLES. The first polygon is formed with the first four vertices, the second polygon with the next four vertices, and so forth. Clearly, the number of coordinate positions given in the vertex list should be a multiple of 4. Any additional positions are ignored.
GL_QUAD_STRIP Generates a series of connected quadrilaterals, similar to the triangle set formed with GL_TRIANGLE_STRIP. For N specified vertices, we obtain N/2 - 1 polygons in the strip. The nth quadrilateral is formed with vertices 2n-1, 2n, 2n+2, and 2n+1, where n = 1, 2, ... , N/2 - 1. Thus, the first quadrilateral is formed with the first four vertices, and each additional pair of vertices defines the next connecting polygon.

Each polygon that we specify has two faces: a back face and a front face. In OpenGL, attributes can be set for each face separately, and back/front identification is needed in both two-dimensional and three-dimensional viewing routines. Therefore, polygon vertices should be specified in a counterclockwise order as we view the "outside" face of the polygon. This identifies the front face for that polygon.

As an example, the following code section generates a filled square version of the polygon that was described using line segments (only edges displayed) in the line-function example of the previous section.

	glBegin (GL_POLYGON); 
	  glVertex2i (50, 50);
	  glVertex2i (150, 50);
	  glVertex2i (150, 150);
	  glVertex2i (50, 150);	  
	glEnd ();

When polygons or other objects are to be specified with a large number of coordinate positions, we can reduce the number of function calls by setting up arrays to store the coordinate data. These arrays are referred to as vertex arrays in OpenGL. Similar arrays can be used for other types of data as well, such as color values.

IMAGES AND BITMAPS

In addition to straight lines and polygons, we can use rectangular grid patterns to define shapes or scenes as a set of two-dimensional color points. The grid pattern could be a scanned image or it could be a shape we constructed by hand or with a program. This primitive is referred to as an image or as pixel data in OpenGL. In some other packages, the grid of color points is called a cell array. We display such a defined shape with the function

	glDrawPixels (width, height, pixelFormat, dataType, image);

Parameters width and height give the dimensions of the color array image. We use parameter pixelFormat to specify the format for the color specification. For example, this parameter could be assigned one of the constants: GL_RGB, GL_RED, GL_GREEN, GL_BLUE, GL_COLOR_INDEX, etc. And we use parameter dataType to specify the type of data in the array. We assign this parameter one of the constants: GL_FLOAT, GL_INT, GL_BYTE, etc.

When a rectangular gird of color points is specified, it is placed into the frame buffer so that the lower left corner of the grid is at the current position. We set the value of the current position with the function

glRasterPos () Sets the Cartesian coordinates for the current position.

Coordinates can two-, three-, or four-dimensional, and we can specify coordinate values in a variety of formats. Suffixes for specifying dimension and format are the same as for the glVertex function. The specified coordinates are transformed to screen coordinates by the viewing routines.

Two other image functions are available in OpenGL. We can store a rectangular section of the frame buffer in a designated array with the function glReadPixels, and we can copy a rectangular section of the frame buffer to another location within the frame buffer with glCopyPixels.

Another method for defining shapes using a rectangular grid is with the function

	glBitmap (width, height, x0, y0, xOffset, yOffset, image);

Parameters width and height are the same as in the function glDrawPixels. Parameter image is now a binary array, so that each pixel position in the grid is displayed at the same color. When the current position is set, the current color setting is applied to the bitmap. Coordinate position (x0, y0) defines the "origin" of the bitmap relative to the lower left corner of the rectangular grid. Coordinate values for the bitmap origin may be positive or negative. The bitmap is displayed by positioning the bitmap origin at the current position within the frame buffer. Values xOffset and yOffset are used as coordinate offsets to update the current frame buffer position after the bitmap is displayed.

CHARACTER GENERATION

Only low-level support is provided in OpenGL for displaying characters and text. One method for generating text is to define each character as a bitmap.

Another method for setting up a text definition is to use a display list, which is simply a set of OpenGL commands. In this approach, the character codes for a particular font are used to index a set of bitmaps. To display a character string, we execute the corresponding display lists.

 

ATTRIBUTES OF OUTPUT PRIMITIVES

COLOR

We can specify color settings for primitives using either the RGB mode (or RGBA mode) or the color-index mode. Once we specify a particular set of color values, these values apply to all subsequently defined primitives until the color settings are changed. A new color then affects only the objects we define after the color change.

In RGB mode, we specify values for the red, green, and blue components of a color. We can also set a fourth color parameter, called the alpha value. The complete, four-dimensional, color specification is then referred to as the RGBA color. This fourth color parameter is used to control color blending for overlapping objects. An important application of color blending is in the simulation of transparency effects. For these calculations, the alpha value corresponds to the transparency coefficient.

To use RGB (or RGBA) mode, we simply set the current color with the function:

glColor () Sets RGB or RGBA values in the range from 0.0 to 1.0.

Integer, floating-point, or other numerical formats can be used to specify the RGB components of a color. Just as with the glVertex function, we use suffix codes such as 3, 4, d, f, and i to indicate color dimension and data format. For example, the following function specifies RGB color components in floating point and sets the current color to the highest intensity blue:

	glColor3f (0.0, 0.0, 1.0);

The default RGB color is white (1, 1, 1), and the default alpha value is 1. Optionally, we can set color values using arrays. In this case, we add a third suffix, v, to the function, and we set a single argument as a pointer to an array of color values. Using an array, we set the color in the above example as

	glColor3fv (colorArray);

Another method for setting color values is to use the color-index mode, which references color tables. We use this mode when we have limited storage available. With this mode, the current color is set from a color table using the function

	glIndex (index);

which specifies an index position within an array of color values. This function also requires a data-type suffix such as d, f, or i for the index value. We can set up a color table with the function

	glIndexPointer (type, offset, ptr);

where parameter type gives the data type, parameter offset gives the spacing between consecutive color values, and parameter ptr is a pointer to the first element in the color array.

POINT ATTRIBUTES

We can set two attributes for points: color and size. Color is controlled by the current color setting, and we set the size for a point with

glPointSize (size); Specifies parameter size as a multiple of the pixel width. We can specify size as any positive floating-point value. If the value for size is not an integer, it is rounded to the nearest integer (assuming antialiasing is disabled). The default value for point size is 1.

LINE ATTRIBUTES

Basic attributes for a straight line segment are line type, line width, and line color. Other possible line effects, such as pen or brush type, can be implemented with adaptions of line type and width functions.

A single color for display of a line segment is determined by the current color settings. Another possibility for color display of a solid line is to vary the color along the line path. One way to do this is to assign a different color to each line endpoint as we define the line, instead of setting a current color for the line as a whole. To illustrate this method, the following code segment assigns the color blue to one endpoint and red to the other endpoint as the endpoint coordinates are defined. The solid line is then displayed as a linear interpolation of the colors at the two endpoints.

	glBegin (GL_LINES); 
	  glColor3f (0.0, 0.0, 1.0);
	  glVertex2i (50,50);
	  glColor3f (1.0, 0.0, 0.0);
	  glVertex2i (250,250);
	glEnd (); 

The default line-type attribute is solid. Lines can also be displayed as dashed, dotted, or dash-dotted. And we can vary the length of the dashes and the spacing between dashes or dots. A particular line type composed of single-color dots or dashes is specified with the OpenGL function

	glLineStipple (repeatFactor, pattern);

which specifies a pixel pattern for line display. Parameter pattern gives a 16-bit binary pattern (1 = pixel on, 0 = pixel off), and integer parameter repeatFactor specifies how many times each bit in the pattern is to be repeated. The pattern is applied to the pixels along the line path starting with the low-order bits in the pattern.

As an example of specifying a line type, suppose parameter pattern is assigned the hexadecimal representation 0x00FF and the repeat factor is 1. This would produce dashed lines with 8 pixels in each dash and 8 pixels in each space between the dashes. Also, since low-order bits are applied first, each line would begin with a dash drawn from the first endpoint along the line path. This would be followed by equal length space-dash combinations until the second endpoint position is reached. With a series of connected line segments, a specified line-type pattern is not restarted at the beginning of each segment. It is applied continuously across all the segments, starting at the first endpoint and ending at the final endpoint for the last segment in the series.

To apply a specified line type composed of dases and dots, we must enable it. Otherwise, the line is displayed with the default pattern: 0xFFFF. Enabling is accomplished with the function:

	glEnable (GL_LINE_STIPPLE);

We turn off a line-type pattern with glDisable.

Line width is set with the function

	glLineWidth (width);

The default value of parameter width is 1, and floating-point values are rounded to the nearest integer when antialiasing is not applied. With antialiasing enabled, fractional width values are possible.

AREA-FILL ATTRIBUTES

For polygons, the basic attribute is the fill style we chose. Polygons can be filled with a single color, a specified fill pattern, or in a "hollow" style by displaying only the edges or the vertex points. And we can select different attributes for the front and back faces of a polygon, reverse the definition of front and back, and eliminate the display of polygons that are either back facing or front facing.

A fill pattern is applied to a polygon with the function

	glPolygonStipple (fillPattern);

Parameter fillPattern is a pointer to a 32-bit by 32-bit mask. A value of 1 in the mask indicates that the corresponding pixel is to be turned on, and a 0 indicates pixel off. The fill pattern is specified in bytes, using the OpenGL data type Glubyte, and pixel intensity is set according to the current color setting. We specify the bit pattern with hexadecimal values, as for example,

	GLubyte fillPattern[] = {
		0xff, 0x00,0xff, 0x00, ...};

The bits must be specified starting with the bottom row of the pattern, and continuing up to the topmost row (32) of the pattern. The pattern is then applied across the current window, filling in the specified polygon where the pattern overlaps the polygon interior. Once we set the current fill pattern with glPolygonStipple, we need to enable the fill routines before we specify the vertices for the polygons that are to be filled with the current pattern. We do this with the statement

	glEnable (GL_POLYGON_STIPPLE);

Similarly, we turn off pattern filling with

	glDisable (GL_POLYGON_STIPPLE);

Another method for filling polygons is to use texture patterns, as discussed in the section on Surface Rendering. Also, the interpolation schemes for surface shading of three-dimensional objects can be adapted to fill the interior of two-dimensional polygons. We obtain an interpolation coloring of a polygon interior just as we did with the line primitive: A color is assigned to each vertex as we specify the polygon shape. As an example, the following code segment assigns a different color to each of the three vertices of a triangle and displays a triangle interior as a linear interpolation of the colors from each vertex.

	glBegin (GL_TRIANGLES); 
	  glColor3f (0.0, 0.0, 1.0);
	  glVertex2i (50, 50);	
	  glColor3f (1.0, 0.0, 0.0);
	  glVertex2i (150, 50);	  
	  glColor3f (0.0, 1.0, 0.0);
	  glVertex2i (75, 150);
	glEnd ();

If a single color is set for the polygon as a whole, the polygon is filled with that one color.

We can also choose to draw only polygon edges, thus displaying the polygon in wireframe or "hollow" form. And we could display a polygon by only plotting the vertex positions. These options are selected with function

	glPolygonMode (face, displayMode;

We use parameter face to select one of the two polygon faces by assigning this parameter one of the following constant values: GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK. Then, if we want only the polygon edges displayed for this face, we assign the constant GL_LINE to parameter displayMode. To plot only the polygon vertex points, we assign the constant GL_POINTS to parameter displayMode. We can also assign this parameter the value GL-FILL, which is the default.

We can only apply rendering routines to convex polygons in OpenGL. For a concave polygon, we must apply a splitting algorithm to convert the polygon into a set of convex polygons, typically all triangles. If we then want to display the split polygon in wireframe form, we need to tell OpenGL which lines are part of the original polygon boundary. We do this with the glEdgeFlag function, which we use to indicate whether or not a vertex is the first endpoint of a boundary edge of the original polygon. The following function is used between glBegin/glEnd pairs.

	glEdgeFlag (GL_FALSE);

This command indicates that the next vertex does not precede a boundary edge. Also, it applies to all subsequently specified vertices until the next call to glEdgeFlag is made. The argument GL_TRUE indicates that a vertex does initialize an edge of the polygon boundary.

The definition of a polygon "front" face is set with the function

	glFrontFace (vertexOrder);

We can choose either a counterclockwise or a clockwise ordering of the vertices as the defining property of a front face. This is done by assigning one of the constant values GL_CCW or GL_CW to parameter vertexOrder . The default ordering is counterclockwise. This function can be used to swap faces of a polygon for which we have specified vertices in a clockwise order.

ANTIALIASING

We activate the antialiasing routines with the function

	glEnable (primitiveType);

where parameter primitiveType is assigned one of the values: GL_POINT_SMOOTH, GL_LINE_SMOOTH, or GL_POLYGON_SMOOTH. For antialiasing color points and fill patterns, we also need to apply blending functions.

INQUIRY FUNCTIONS

We can retrieve values for current attribute settings and for various other parameters using an appropriate "Get" function such as glGetIntegerv (), glGetFloatv (), glGetPolygonStipple ().

For instance, we can retrieve the current color settings with

	glGetFloatv (GL_CURRENT_COLOR, colorArray);

or we could get the current position using the constant GL_CURRENT_RASTER_POSITION. Similarly, we could check the range of point sizes or line widths that are supported using the constants GL_POINT_SIZE_RANGE and GL_LINE_WIDTH_RANGE. And we could also check whether certain routines, such as antialiasing, were enabled or disabled.

 

GEOMETRIC TRANSFORMATIONS

The basic geometric transformation functions in OpenGL are

glTranslate (tx, ty, tz); Specifies a translation vector.
glRotate (theta, vx, vy, vz); Specifies a rotation angle about a rotation-axis vector (vx, vy, vz), with the axis passing through the coordinate origin.
glScale (sx, sy, sz); Specifies multiplicative factors relative to the coordinate origin for either coordinate scaling or reflection.

Each of these functions produces a 4 by 4 transformation matrix that can be applied to the two-dimensional or three-dimensional vertices of an output primitive. For two-dimensional applications, we set tz = 0, (vx, vy, vz) = (0, 0, 1), and sz = 1. Reflection transformations are performed by setting one or more of sx, sy, and sz to a negative value. To indicate the numerical format of the values that are to be assigned to the transformation parameters, we affix a suffix of f (float) or d (double) to the transformation functions. For example, we obtain a two-dimensional translation along a 45-degree line in the xy plane with

	glTranslatef (25.0, 25.0, 0.0);

Geometric transformation functions are applied to objects as matrix multiplications. And, in OpenGL, the jk component of a matrix, represents the element in the jth column and kth row. As each transformation function is specified, it’s matrix representation is pre-multiplied by the current matrix. After all transformations are specified, coordinate positions are transformed by the composite matrix. We can initialize the current matrix to the identity matrix with

glLoadIdentity (); Sets the current matrix to the 4 by 4 identity matrix I.

Or we can set the elements ot the current matrix to some other values:

glLoadMatrix (M); Specifies the current matrix as M, which is a predefined 4 by 4 matrix or a 16-element vector.

We can also specify any matrix for concatenation with the current matrix. This is accomplished with the function

glMultMatrix (M); Post-multiplies the current matrix by matrix M.

We indicate data type for the matrices to be loaded or multiplied in the above two commands with suffix d or f.

Before initializing the current matrix or specifying transformations, we set the mode for the matrix operations. This is accomplished with the function

	glMatrixMode (GL_MODELVIEW);

Other mode constants are used to indicate matrix operations for viewing and for texture mapping. A complete set of geometric transformation statements might have the form

	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();;
	glMultMatrixf (M1);
	glMultMatrixf (M2);

This set of statements would be followed by an object definition that would be transformed with the matrix product M1M2.

 

INTERACTIVE INPUT USING GLUT FUNCTIONS

glutReshapeFunc (func); Specifies a function that will accept new width and height values for a window that is to be moved or resized. Also, this function can be used to reset the size of the display screen area (viewport).
glutKeyboardFunc (func); Specifies a function that is to be executed when a particular key character is selected. This function also can return the mouse position in window coordinates.
glutMouseFunc (func); Specifies a function that is to be executed when a mouse button is pressed or released, and returns the mouse position in window coordinates.
glutMotionFunc (func); Specifies a function that is to be executed when a mouse pointer moves within the window while one or more buttons is pressed. The mouse position is returned in window coordinates.

 

VIEWING TRANSFORMATIONS

TWO-DIMENSIONAL VIEWING

Once a display "window" is activated, as discussed in the first section of this supplement, we need to define a clipping "window" and the viewport coordinates for the window-to-viewport transformation. We also need to specify a transformation type for matrix operations (which is GL_PROJECTION in this case), and we need to initialize the transformation matrix.

Viewport parameters are specified with the function

	glViewport (xvmin, yvmin, vpWidth, vpHeight);

where all parameter values are given in integer, screen coordinates within the display window. The default position for the lower left corner of the viewport is (0, 0), and the default size is the width and height of the display window.

We set the matrix mode and initialize the transformation matrix to the identity matrix with the two statements:

	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();

We can then define a two-dimensional clipping window with the function

	gluOrtho2D (xwmin, xwmax, ywmin, ywmax);

which specifies the min and max coordinate positions as double-precision numbers. This function sets up the transfomation matrix for mapping objects within the clip window to the specified viewport. Another way to set coordinates for a clipping window is with the function:

	glOrtho (xwmin, xwmax, ywmin, ywmax, -1.0, 1.0);

As discussed in the section on Three-Dimensional Viewing, this function is used to set a clipping volume (view volume). The last two parameters set the clipping range for the z direction. Since z = 0 for two-dimensional applications, no objects will be outside the z clipping range: -1 to 1.

THREE-DIMENSIONAL VIEWING

We set the mode for three-dimensional viewing transformations with the same function that we used for two-dimensional viewing operations:

	glMatrixMode (GL_PROJECTION);

And, just as in two-dimensional applications, we need to initialize the transformation matrix to the identity matrix.

In OpenGl, we set up a viewing system with the following three parameters: a view reference position (also called the viewpoint, or "eye" position) P0, a "look-at" position Pref, and the view-up vector V with the function

	gluLookAt (x0, y0, z0, xref, yref, zref, Vx, Vy, Vz);

The direction of viewing is then given by the vector Pref - P0, from the viewpoint to the look-at point. Default values are the world-coordinate origin (0, 0, 0) for the viewpoint, the negative z axis for the viewing direction, and the world-coordinate y direction for the view-up vector. A viewing matrix is set up for these parameters and is pre-multiplied by the current matrix. This function is in the Utility Library because it uses a combination of rotation and translation routines to produce the matrix for transforming from world to viewing coordinates.

Since viewport coordinates are assigned in two-dimensional screen coordinates, we use the same function for both two-dimensional and three-dimensional viewing. Thus, we set the position and size of the viewport with glViewport, which was discussed in the last section.

We specify parameters for an orthographic, parallel projection with

	glOrtho (xwmin, xwmax, ywmin, ywmax, dNear, dFar);

which sets the clip-window coordinates and gives the distances to the near and far clipping planes from the viewpoint. The clipping plane distances dNear and dFar are positive if the clipping planes are to be positioned along the direction of view. If the clipping plane distances are negative, the clipping planes are "behind" the viewpoint.

A perspective transformation is specified with the function

	glFrustum (xwmin, xwmax, ywmin, ywmax, dNear, dFar);

which has the same parameters as the orthographic, parallel projection function. For a perspective projection, the near and far clipping plane distances must be positive.

An alternative specification for a perspective transformation is with the function

	gluPerspective (fovAngle, aspectRatio, dNear, dFar);

Parameter fovAngle represents the "camera" field-of-view angle, which is the angle at the viewpoint that the clipping window subtends. In other words, the field-of-view angle is a measure of the width of the clipping window. We can assign to parameter fovAngle any value between 0 and 180o. A large field-of-view angle simulates a camera with a wide-angle lens. Parameter aspectRatio is used to specify the width-to-height ratio of the clipping window. This value should match that of the viewport. Distances to the near and far clipping planes are specified with the last two parameters in this function.

As an option in OpenGl, we can also set up arbitrary clipping planes with the following functions. Parameters for this function are a clip-plane identification number and an array name that lists values for the plane-equation parameters A, B, C, and D:

	glClipPlane (id, paramArray); 
	glEnable (id);

Parameter id can be assigned values GL_CLIP_PLANE0, GL_CLIP_PLANE1, and so forth up to a facility defined maximum.

 

VISIBLE-SURFACE IDENTIFICATION

As a preliminary to viewing only the visible surfaces in a scene, we can eliminate the back-facing surfaces with the functions

	glEnable (GL_CULL_FACE);
	glCullFace (GL_BACK);

There are other options we can use for culling object surfaces in OpenGL. Arguments for the glCullFace function can also be GL_FRONT or GL_FRONT_AND_BACK.

We apply the depth buffer method for viewing only visible surfaces by enabling the depth-testing routines in OpenGL and clearing the depth buffer. This is accomplished with the following statements:

	glEnable (GL_DEPTH_TEST); 
	glClear (GL_DEPTH_BUFFER_BIT); 

We only need to enable depth testing once in a program, but we need to clear the depth buffer each time we want to redisplay a scene. Also, we can clear both the color buffer and the depth buffer at the same time with

	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

It is possible in OpenGL to use the depth test to view more distant objects instead of the closest objects. In fact, OpenGL provides a variety of options for the depth test. When we compare a depth value to the current buffer value for a given pixel position, we can save the depth value if it is greater than the buffer value, equal to the buffer value, and so forth. We select an option for depth testing with the function

	glDepthFunction (depthTestOption);

Parameter depthTestOption can be assigned any of the values GL_LESS, GL_GREATER, GL_EQUAL, GL_LEQUAL, GL_GEQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER. The default depth-test option is GL_LESS.

OpenGL provides no functions for directly testing for wireframe visibility. But we can adapt the depth-buffer method to view only the visible edges in a scene. To implement this approach, we can display all surface edges in a foreground color, and then fill the polygon interiors with the background color. With depth buffering enabled, only visible edges will be displayed providing we have only simple surfaces in the scene. If surface edges overlap the interior region for some objects, we would need to apply other methods for viewing only the visible parts of a wireframe display.

 

ILLUMINATION MODELS AND SURFACE RENDERING

LIGHT SOURCES

To illuminate a scene in OpenGL, we set up light "sources" that have a variety of properties, such as position, color, and direction. We set these light-source properties with

	glLight (id, property, propertyValue);

Parameter id is assigned an OpenGl constant value that names the light source. Depending on capabilities at a particular facility, we can set from one to eight different light sources in a scene, using the id names GL_LIGHT0, GL_LIGHT1, up to GL_LIGHT7. Parameter property is assigned one of the ten property code names allowed for OpenGL light sources. We set each property value for a particular light source with a separate call to glLight which specifies the id, the appropriate OpenGL property name, and the property value. A suffix, f (float) or i (integer), is used with this function to indicate the data type for the assigned property value. For position, intensity, and directional values, parameter propertyValue is a pointer to an array of values. For the assignment of array values, we also need to add the suffix v to function glLight.

We set the position of a source by assigning parameter property the constant GL_POSITION and assigning parameter propertyValue an array name that lists the x, y, z, and h values. For example,

	glLightfv (GL_LIGHT1, GL_POSITION, ptCoords);

If the homogeneous parameter h is not assigned a value of 0, then the coordinate position (x, y, z) sets the location for a point light source. In OpenGL, this is called a positional light source. Otherwise, with h = 0, the light source is considered to be very far away (at "infinity") so that the rays from the light source are all parallel. This is called a directional light source in OpenGL. The direction for the light rays in this case is defined as the vector from (x, y, z) to the viewing origin. The default value for the position parameter is (0, 0, 1, 0), which defines a distant source with ray direction parallel to the negative z direction. Light-source positions must be set before we specify the viewing transformation. This ensures that the position of light sources and the viewpoint are both transformed the same way.

Three different sets of RGB colors can be assigned to a light source in OpenGL, instead of a single RGB light intensity that an actual source would have. In this empirical scheme, the three color properties provide additional options for varying the lighting effects in a scene. These properties are called GL_AMBIENT, GL_DIFFUSE, and GL_SPECULAR, and they are used in surface rendering calculations to obtain diffuse and specular lighting effects. The ambient RGB value for a light source contributes to the background, or ambient, lighting in a scene; the diffuse component of the source contributes to the diffuse lighting effects; and the specular component contributes to the specular effects. Default colors for GL_LIGHT0 are black for the ambient component and white for the diffuse and specular components. Black is the default color for all three components of any other light source, such as GL_LIGHT1, GL_LIGHT2, etc. For realistic lighting effects, the diffuse and specular components of any light source should be set to the same RGB values, typically with the ambient component turned off (black). As an example of setting a color component, the following two statements assign the color white to the specular property of light source 2.

	GLfloat rgbaSpecValues[] = {1.0, 1.0, 1.0, 1.0};
	glLightfv (GL_LIGHT2, GL_SPECULAR, rgbaSpecValues);

Another property that we can set for OpenGL light sources is the spotlight effect. For nearby ("positional") lights, we can restrict the light emission to a cone shape by specifying the spotlight direction (cone axis) and a cone angle. Spotlight direction is given as the three components (vx, vy, vz) of a vector which defines the cone axis. This vector value is assigned to the property GL_SPOT_DIRECTION. The default spotlight direction is parallel to the negative z axis: (0, 0, -1). Spotlight size is given as the angular spread of the cone, measured from the cone axis to the edge of the cone. Cone angle must be either a value in the range from 0o to 90o or the value 180o, and a selected angular value is assigned to the property GL_SPOT_CUTOFF. The default spotlight angle is 360o, which gives a source that emits light in all directions. In the following example, a light source is defined to be a spotlight that points in a direction parallel to the x axis and has a cone angle of 30o.

	GLfloat spotDirVector[] = {1.0, 0.0, 0.0, 1.0};
	glLightfv (GL_LIGHT3, GL_SPOT_DIRECTION, spotDirVector);
	glLightf (GL_LIGHT3, GL_SPOT_CUTOFF, 30.0);

Nearby, or "positional", light sources can also be assigned values for intensity attenuation calculations. Intensity attenuation is performed in the empirical OpenGL lighting model as an inverse quadratic function, where distance d is measured from the light position to a point on an object surface. Each of the three attenuation coefficients (a0, a1, and a2) are separately assigned either integer or floating-point values. The corresponding light-source properties are named: GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, and GL_QUADRATIC_ATTENUATION. Default values for the attenuation coefficients are a0 = 1, a1 = 0, and a2 = 0.

GLOBAL LIGHTING PARAMETERS

Another lighting parameter that can be set in OpenGL is the global level for the ambient, or background, lighting. (This is in addition to the ambient light component that can be set for each OpenGL light source.) We can also specify whether the specular-reflection calculations are to be performed assuming a "local" viewing position or one that is infinitely far away, and we can apply surface rendering to front faces only or to both front and back faces of surfaces. These global effects are set with the following function, in the same way that we set properties for light sources.

	glLightModel (globalProperty, globalPropertyValue);

Parameter globalProperty takes one of the following OpenGL constant values: GL_LIGHT_MODEL_AMBIENT, GL_LIGHT_MODEL_LOCAL_VIEWER, GL_LIGHT_MODEL_TWO_SIDE. The level of the general ambient light for a scene is assigned as an array of RGBA values. Default for the ambient light is (0.2, 0.2, 0.2, 1.0). The local-viewing property can be assigned either the value GL_TRUE or the value GL_FALSE (or 0, or 0.0). The value GL_FALSE causes the view direction to be set as parallel to the negative z direction. Otherwise (GL_TRUE), we obtain "local viewing" with the view position at the origin of the viewing ("eye") coordinates. Default value for the local-viewing parameter is GL_FALSE. And we select two-sided or one-sided lighting with the values GL_TRUE and GL_FALSE, respectively. The default here is GL_FALSE (one-sided lighting only). Suffixes for the glLightModel function are again f, i, and v, depending on the data type for parameter globalPropertyValue.

SURFACE REFLECTION AND EMISSION PROPERTIES

We set the illumination properties for the front and back faces of a surface with

	glMaterial (face, surfaceProperty, surfacePropertyValue);

These properties are set at each vertex as we specify the vertex coordinates. Suffixes for this function are f (float), i (integer), and v (vector). All data values except the specular-reflection exponent are given as arrays. Reflection coefficients in OpenGL are named GL_AMBIENT, GL_DIFFUSE, and GL_SPECULAR. Also, since ambient light is diffuse, we can set both coefficients to the same value with the property GL_AMBIENT_AND_DIFFUSE. Default values are (0.2, 0.2, 0.2, 1.0) for the ambient-reflection coefficient, (0.8, 0.8, 0.8, 1.0) for the diffuse-reflection coefficient, and (0.0, 0.0, 0.0, 1.0) for the specular-reflection coefficient. The specular-reflection exponent for highlight effects is called GL_SHININESS, and the default exponent value is 0. We can set this exponent to any value in the range from 0 to 128.

SURFACE RENDERING

We have two options in OpenGL for surface rendering: constant-intensity (flat) shading and Gouraud ("smooth") shading. Once color values have been assigned to polygon vertices, we select a shading type with

	glShadeModel (renderingType);	

Parameter renderingType is assigned either the value GL_FLAT or the value GL_SMOOTH. The default surface rendering is Gouraud shading ("smooth").

We can also apply texture patterns in OpenGL to any primitive, such as a set of points, a straight line segment, or a polygon surface, or to any curved line or curved surface. Texture patterns can be one dimensional or two dimensional, and we specify various parameters for a pattern either with glTexImage1D or with glTexImage2D. These functions give the size, format, data type, pattern array name, and other information. Other functions are available for performing pattern manipulations, such as scaling, copying a pattern from the frame buffer, and modifying an existing pattern. We apply a pattern to an object by enabling the texture routines and by specifying texture coordinates along with the object vertex coordinates. The texture coordinates determine how a pattern is to be applied to an object.

Transparency is simulated by combining color values from different objects using the alpha component of the color values. The alpha component is assigned a value between 0 and 1, and it corresponds to a transparency coefficient in color-blending applications. For the transparency calculations, we need to specify objects in a scene from back to front. As each object in turn is processed, we then blend its surface color with the currently stored raster color. We accomplish this with the set of commands:

	glEnable (GL_BLEND);
	glBlendFunc (surfaceParameter, rasterParameter);

The two parameters in this function provide a number of options for color blending. For the simulation of transparency, we can set surfaceParameter to the value GL_SRC_ALPHA and rasterParameter to the value GL_ONE_MINUS_SRC_ALPHA. In OpenGL, the current surface is referred to as the "source" and the current pixel color stored in the frame buffer is referred to as the "destination". Thus, these settings for the blend function arguments indicate that the pixel color ("destination") should be multiplied by the transparency coefficient (alpha value) of the current surface ("source"), and the surface color should be multiplied by one minus the transparency coefficient. The result is then added and normalized to a value in the range from 0 to 1. This color is then stored as the new pixel color, and the next surface is processed. We can set a number of other options for the glBlendFunc parameters to provide various color-blending combinations.

OpenGL provides no built-in routines for producing shadows in a scene. To determine shadow regions for a light source, we can set the viewpoint at the light-source position and determine which surface sections cannot be "seen" from that position. These surfaces can then be tagged so that only the global ambient lighting (and possibly lighting effects from other sources) is applied.

ACTIVATING THE LIGHTING EFFECTS

To apply surface rendering with selected lighting conditions, we need to "turn on" the OpenGL lighting effects. Otherwise, objects are simply displayed using current color settings. We activate lighting effects with the function

	glEnable (GL_LIGHTING);

 

REFERENCES

OpenGL Programming Guide, Second Edition
Mason Woo, Jackie Neider, and Tom Davis
Addison-Wesley Developers Press, 1997

OpenGL Reference Manual, Second Edition
OpenGL Architecture Review Board Editors: Renate Kempf and Chris Frazier
Addison-Wesley Developers Press, 1997