Chapter 4
Perspective
Orthographic, or parallel, projection is fine for many purposes, but when the goal is realism 
in the graphics, perspective projection may be more appropriate. In the real world, the fact that 
objects appear smaller when more distant is universally familiar. Artists recognize that 
elements of a scene appear to come together to a vanishing point at some great distance. This 
chapter shows how to produce this realistic effect and to position vertices in such an 
environment.
Illustration
With OpenGL the programmer can construct a (you guessed it!) matrix to project a scene 
onto the computer screen in a manner that simulates perspective. This process is called 
(appropriately) perspective projection. Consider two parallel lines as in the following code:

  glBegin(GL_LINES);	
    glVertex3f(-0.5,0.1,-2.2);	
    glVertex3f(-0.5,15.0,-32.2);	
    glVertex3f(+0.5,0.1,-2.2);	
    glVertex3f(+0.5,15.0,-32.2);	
  glEnd;	
The two line segments run side by side off into the z distance, rising in the y direction. 
Figure 4-1 shows how they appear with perspective projection. Although reproducing this 
effect requires more code than given here, this is enough to show that the two lines are 
indeed parallel. The apparent convergence of the lines is due entirely to the perspective 
projection.


PAGE 61
 Command
To set up the perspective projection matrix, use this command:

procedure glFrustum(left,right,bottom,top,zNear,zFar:GLdouble);stdcall;	
Clipping Volume
What in the world is a frustum? Basically, a frustum is a pyramid with the top chopped off. 
Since the clipping volume looks like a frustum with a rectangular base, lying horizontally, 
the command name makes sense. Figure 4-2 shows the shape of the clipping volume.


PAGE 62
 Parameters
Notice that the parameters for glFrustum are the same as the parameters for glOrtho. In 
general these parameters have a similar usage, but there are enough differences between 
them to warrant caution. Just as in glOrtho, left, right, bottom, and top define a rectangle. In 
glOrtho, however, the front rectangle and the rear rectangle are identical except for their z 
value, so only one rectangle specification is sufficient. In glFrustum, the near and the far 
rectangles are very different sizes, but the command only takes the values for the near 
rectangle.
How in the world does this command define the far rectangle? The answer lies in the usage 
of the znear and zfar parameters. The znear value is the distance from the viewer to the near 
face of the frustum, which is the “cut” across the pyramid. The znear distance must be 
expressed as a positive number. The zfar value is the distance to the farther face of the 
frustum (base of the pyramid), which must be a positive number larger than znear. In effect 
the viewer is at the apex of the pyramid, so znear and zfar are enough to define the base of 
the frustum from its front face, as Figure 4-2 suggests.
Depth
Previous Cube
Create a directory (under the OpenGL directory, of course) and name it Chapter.4. Under 
that directory create a directory named Multi. Copy the files from OpenGL\Chapter.3\Multi 
to OpenGL\Chapter.4\Multi. Open this project in Delphi and edit the FormCreate method 
in Multi.Pas. Replace

  glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0); 	
with

  glMatrixMode(GL_PROJECTION);	
  glFrustum(-1.0,1.0,-1.0,1.0,2.0,37.0); 	
This command change maps the same near face rectangle, but specifies the z range 
differently, as well as providing a different projection. The clipping volume starts at 2.0 units 
from the viewer and extends to 37.0 units from the viewer. In glOrtho, znear and zfar just 
provided a range of values whose meaning was entirely up to the programmer. With parallel 
projection, apparent sizes are not affected by distance from the viewer, so that distance is not 
a consideration. With perspective projection, distance from the viewer is a primary 
ingredient.

PAGE 63
 Compile and run. Nothing but the green background shows because under the new 
coordinate system the cube was constructed entirely in front of the front clipping face. Use 
transformations to bring the cube into view. Fill out the parameters as in Figure 4-3, click 
OK, and select Transformation from the menu.
Now the cube shows, but it looks a little strange. It is like the back is larger than the front. 
Test this theory with a rotation transformation. Set it up as in Figure 4-4.
Sure enough, the back of the cube appears larger than the front. Since this is the opposite of 
what perspective projection should produce, something must be wrong. This is a good time 
to test the coordinates.
Test
Like the depth test in Chapter 3 draw two rectangles in the FormPaint method, replacing the 
current contents. The reason for the choice of z coordinates will soon become evident. Save 
the project as Depth.Dpr in OpenGL\Chapter.4\Depth and save the main unit as 
Depth1.Pas in the Depth directory also. Remove the Trans1 unit from the project, remove the 
uses Trans1 statement from the main unit, remove the Main Menu control, and empty the 
contents of the menu item event handlers. Adjust the form’s ClientHeight back to 340. Here 
is what FormPaint should look like:


PAGE 64
 procedure TForm1.FormPaint(Sender: TObject);	
begin	
  if not openGLReady then	
    exit;	
	
  {green background}	
  glClearColor(0.0,0.4,0.0,0.0);	
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);	
	
  glBegin(GL_POLYGON); {near face}	
    glColor3f(0.5,0.0,0.0);	
    glVertex3f(-0.4,-0.4,-2.4);	
    glVertex3f(+0.4,-0.4,-2.4);	
    glVertex3f(+0.4,+0.4,-2.4);	
    glVertex3f(-0.4,+0.4,-2.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {back face}	
    glColor3f(0.0,0.0,0.5);	
    glVertex3f(-0.2,-0.2,-2.8);	
    glVertex3f(+0.6,-0.2,-2.8);	
    glVertex3f(+0.6,+0.6,-2.8);	
    glVertex3f(-0.2,+0.6,-2.8);	
  glEnd;	
	
  glFlush;	
	
  errorCode := glGetError;	
  if errorCode<>GL_NO_ERROR then	
    raise Exception.Create(‘Error in Paint’#13+	
      gluErrorString(errorCode));	
end; {FormPaint}	
Save, compile, and run. Again the red rectangle is in front of the blue rectangle. But look 
again at the z coordinates of the two rectangles. The farther (blue) rectangle is more negative 
in the z direction than the near rectangle. In the orthographic projection clipping volume a 
more distant point is more positive (or less negative) than a nearer point, and the 
specifications of the cube vertices took that fact into account. So when the same vertices 
appeared in a perspective projection clipping volume whose z coordinates run in the 
opposite direction, naturally the cube was distorted.
Better Cube
In Delphi load the previous project under Chapter.4, Multi.Dpr. Adapt the coordinates of the 
cube vertices to the new z direction reality. Remember that moving away from the viewer is 
to travel in the negative z direction, 

PAGE 65
 even though the znear and zfar parameters are positive and more positive, respectively. 
Make the FormPaint method look like this:

procedure TForm1.FormPaint(Sender: TObject);	
begin	
  if not openGLReady then	
    exit;	
	
  {green background}	
  glClearColor(0.0,0.4,0.0,0.0);	
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);	
  glBegin(GL_POLYGON); {near face}	
    glColor3f(0.5,0.0,0.0);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(+0.4,-0.4,+0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
    glVertex3f(-0.4,+0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {right face}	
    glColor3f(0.3,0.3,0.8);	
    glVertex3f(+0.4,-0.4,+0.4);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {left face}	
    glColor3f(0.5,0.0,0.5);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(-0.4,+0.4,+0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {back face}	
    glColor3f(0.0,0.0,0.5);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {bottom face}	
    glColor3f(0.5,0.5,0.0);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(+0.4,-0.4,+0.4);	


PAGE 66
   glEnd;	
	
  glBegin(GL_POLYGON); {top face}	
    glColor3f(0.3,0.0,0.1);	
    glVertex3f(-0.4,+0.4,+0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
  glEnd;	
	
  glFlush;	
	
  errorCode := glGetError;	
  if errorCode<>GL_NO_ERROR then	
    raise Exception.Create(‘Error in Paint’#13+	
      gluErrorString(errorCode));	
end; {FormPaint}	
For convenience, move the cube inside the clipping volume with a call to glTranslatef near 
the bottom of the FormCreate method. Place it there so it will only happen once.

  glLoadIdentity;	
  glTranslatef(0.0,0.0,-2.4);	
	
  glEnable(GL_CULL_FACE);	
  glCullFace(GL_BACK);	
end; {FormCreate}	
Movement
Rotation
Set up a transformation using the parameters of Figure 4-4, then perform the rotation. Notice 
that since the cube is so close to the front clipping face, the rotation sends a corner right 
outside the clipping volume. Now it is apparent that clipping works just like in the 
orthographic projection clipping volume. Notice too that back face culling also works as 
expected. The exposed interior of the cube reveals a green nothing. Select Cull Disable from 
the menu and behold the interior faces of the cube.
Translation
In the parameter screen uncheck Rotate and check Translate. Set z to –0.1 and the rest to 0.0. 
Click OK, and select Transformation from the menu repeatedly until the entire cube is inside 
the clipping volume. Now the 

PAGE 67
 image, shown in Figure 4-5, demonstrates clearly that the far side of the cube appears 
smaller than the near side.
Next press and hold Alt+T so that the keyboard starts repeating. Watch the cube retreat into 
the distance. Now that’s perspective!


        Figure 4-5. Near and far sides of a
        cube in perspective.

Multiple Objects
Matrix Stack
Most scenes built with OpenGL consist of more than just a single object on a plain 
background. Building various objects in the right places is an obvious way to build more 
complex scenes, but it can get tedious. Suppose the scene has a number of objects that are 
similar to each other and the programmer has to describe them over and over. Isn’t 
computer software supposed to relieve people of certain tiresome repetitive tasks? OpenGL 
provides a way!
Put a commonly used object in its own procedure or method. Build it around the origin 
(0.0,0.0,0.0) for ease of coding. Rotate it, translate it, even scale it, to put it in the right place, 
with the right orientation, and in the right shape. Then (this is important!) restore the 
ModelView matrix to its former condition and build and place another object.
What programming structure useful for saving and restoring something comes to mind? A 
stack would be nice. Indeed, as mentioned previously, OpenGL even provides a matrix 
stack. The commands for storing the current matrix on the stack, and retrieving it, are:

procedure glPopMatrix; stdcall;	
procedure glPushMatrix; stdcall;	

PAGE 68
 Reuse an Object
Save the Mult project as Obj.Dpr in a new directory named Objects under Chapter.4. 
Remove Trans1.Pas from the project and save Multi.Pas as Objct.Pas in the new directory. 
Delete the menu object from the form, empty all the menu item event handlers, and adjust 
the ClientHeight of the form back to 340.
The object of choice these days is a cube. To make it available for reuse, collect the code to 
draw the cube and put it in a separate method.

procedure TForm1.Cube;	
begin	
  glBegin(GL_POLYGON); {near face}	
    glColor3f(0.5,0.0,0.0);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(+0.4,-0.4,+0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
    glVertex3f(-0.4,+0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {right face}	
    glColor3f(0.3,0.3,0.8);	
    glVertex3f(+0.4,-0.4,+0.4);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {left face}	
    glColor3f(0.5,0.0,0.5);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(-0.4,+0.4,+0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {back face}	
    glColor3f(0.0,0.0,0.5);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {bottom face}	
    glColor3f(0.5,0.5,0.0);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	


PAGE 69
     glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(+0.4,-0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {top face}	
    glColor3f(0.3,0.0,0.1);	
    glVertex3f(-0.4,+0.4,+0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
  glEnd;	
end;	
Declare the method at the end of the private section of the form:

  private	
    GLContext: HGLRC;	
    glDC: HDC;	
    errorCode: GLenum;	
    openGLReady: boolean;	
    oldw,	
    oldh: integer;	
    procedure ExceptionGL(Sender:TObject;E:Exception);	
    procedure Cube;	
  public	
Make a Scene
This is enough information to build a scene with multiple copies of the cube. Make the 
FormPaint method look like this:

procedure TForm1.FormPaint(Sender: TObject);	
begin	
  if not openGLReady then	
    exit;	
	
  {background}	
  glClearColor(0.6,0.8,0.8,0.0);	
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);	
	
  glPushMatrix;	
  glTranslatef(-0.7,0.7,-2.8);	
  Cube;	
  glPopMatrix;	
	
  glPushMatrix;	
  glTranslatef(1.9,-1.6,-5.4);	
  Cube;	
  glPopMatrix;	
	


PAGE 70
   glFlush;	
	
  errorCode := glGetError;	
  if errorCode<>GL_NO_ERROR then	
    raise Exception.Create('Error in Paint'#13+	
      gluErrorString(errorCode));	
end; {FormPaint}	
Notice the sequence: Push the ModelView matrix, translate to the desired location, call the 
routine that builds the object about the origin, and pop the matrix. Do this for each object in 
the scene; even if a given object is not repeated, it is usually simpler to construct at the 
origin, especially if it has symmetry. In this simple example only a translation appears before 
each cube, but any combination of the three transformations could take its place.
Save, compile, and run. Behold a 3-D, two-object scene! Notice the marvelous new 
background color. Perhaps its name should be “Clear Blue Sky with Light Smog.” Figure 4-6 
shows how it looks. The new color, though far from pretty, provides better contrast for the 
cubes.

Independent Movement
The techniques for creating multiple copies of an object and placing them in different places 
also lend themselves to moving the objects separately. To demonstrate, add some fields to 
the private section of the form type declaration:

    x1,y1,z1,x2,y2,z2,xr1,yr1,zr1,	
    xr2,yr2,zr2,angle1,angle2,sign: glFloat;	
Initialize sign at the very bottom of the FormCreate method:

  sign := 0.1;	

PAGE 71
 Translate
Remove the glTranslate command from the FormCreate method, then in the FormPaint 
method replace

  glTranslatef(-0.7,0.7,-2.8);	
with

  glTranslatef(x1,y1,z1);	
Also replace

  glTranslatef(1.9,-1.6,-5.4);	
with

  glTranslatef(x2,y2,z2);	
Now that the arguments in the glTranslatef calls are variables, code to manipulate the 
variables is appropriate. Using the Object Inspector, set the form’s KeyPreview property to 
true. On the Events page, double-click the OnKeyPress event and fill in the following event 
handler:

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);	
var	
  ok: boolean;	
begin	
  ok := true;	
  case Key of	
    '+': sign := 0.1;	
    '-': sign := -0.1;	
    'X': x1 := x1+sign;	
    'x': x2 := x2+sign;	
    'Y': y1 := y1+sign;	
    'y': y2 := y2+sign;	
    'Z': z1 := z1+sign;	
    'z': z2 := z2+sign;	
    else	
      ok := false;	
  end; {case}	
  if ok then	
    Invalidate;	
end;	
Whenever the user executes a keystroke, the FormKeyPress method tests the character in the 
case statement and adjusts the variables accordingly. Pressing  “+” or “–” sets the sign 
variable accordingly. The letters “x,” “y,” and “z” set the corresponding variables, with the 
capital letters setting x1, etc., and the lowercase letters setting x2, etc. If a desired letter came 

PAGE 72
 through, then a call to Invalidate causes the FormPaint method to fire again.
The sign variable starts with a positive value of one-tenth. Pressing “–” changes it to 
negative one-tenth and pressing “+” changes it back. This value adds to the other variables 
in response to the appropriate letter. Save, compile, and run. Start by pressing the minus 
key, then pressing and holding a capital “Z” until a cube comes into view. Since the 
variables are fields of the form, they start as 0.0 unless explicitly changed. That means both 
cubes remain out of sight at the origin until the user gives them a lot of negative z. Capital 
letters affect one cube and lowercase letters affect the other cube. Experiment with pressing 
the desired keys and enjoy the power of moving the two cubes independently.
Rotate
Being able to move two cubes independently represents a definite advance in the available 
OpenGL power, but rotating the cubes separately is an even greater advance. Better yet is 
the power to combine rotation and translation for each cube (independently). The variables 
have already been declared. After each glTranslatef goes a glRotatef:

  glTranslatef(x1,y1,z1);	
  glRotatef(angle1,xr1,yr1,zr1);	
	
  glTranslatef(x2,y2,z2);        	
  glRotatef(angle2,xr2,yr2,zr2);	
The case statement in the FormKeyPress method needs an addition so that it can manipulate 
the rotation variables. The letters to check are upper- and lowercase “r,” “s,” and “t.”

    'a': angle2 := angle2+10.0*sign;	
    'r': xr2 := xr2+sign;	
    's': yr2 := yr2+sign;	
    't': zr2 := zr2+sign;	
    'A': angle1 := angle1+10.0*sign;	
    'R': xr1 := xr1+sign;	
    'S': yr1 := yr1+sign;	
    'T': zr1 := zr1+sign;	
The letters “r,” “s,” and “t” set up the axis of rotation for each cube. Each axis is a vector (line 
segment with direction) from (0.0,0.0,0.0) toward the vertex constructed from (xr1,yr1,yr1) or 
(xr2,yr2,zr2). The letter “a” adjusts the amount of rotation (angle1 or angle2) about the 
appropriate vector. It would be nice to have a little help visualizing the axes of rotation. 

PAGE 73
 Add the following method to draw a representation of the coordinate axes along with the 
two rotation vectors:

procedure TForm1.Axes;	
begin	
  glLineWidth(3.0);	
  glBegin(GL_LINES);	
    glColor3f(0.0,0.0,0.0);	
	
    glVertex3f(+0.0,+0.0,+0.4);	
    glVertex3f(+0.0,+0.0,-0.4);	
	
    glVertex3f(+0.4,+0.0,+0.0);	
    glVertex3f(-0.4,+0.0,+0.0);	
	
    glVertex3f(+0.0,+0.4,+0.0);	
    glVertex3f(+0.0,-0.4,+0.0);	
  glEnd;	
	
  glLineWidth(1.0);	
  glBegin(GL_LINES);	
    glColor3f(1.0,1.0,0.0);	
    glVertex3f(0.0,0.0,0.0);	
    glVertex3f(xr1,yr1,zr1);	
	
    glColor3f(0.0,1.0,1.0);	
    glVertex3f(0.0,0.0,0.0);	
    glVertex3f(xr2,yr2,zr2);	
  glEnd;	
end;	
Declare the new method at the bottom of the form’s private section:

    procedure ExceptionGL(Sender:TObject;E:Exception);	
    procedure Cube;	
    procedure Axes;	
  public	
    { Public declarations }	
  end;	
Call the Axes method inside the FormPaint method, just before the call to glFlush. Translate 
it along the z-axis to bring it inside the clipping volume. Translate it a little along the x and y 
axes so that the viewer will see it slightly from above and from the side, to make its three-
dimensional nature more evident.

  glPushMatrix;	
  glTranslatef(-0.6,-0.6,-2.8);	
  Axes;	


PAGE 74
   glPopMatrix;	
	
  glFlush;	
Save, compile, and run. In addition to all the previous abilities of the program, it can now 
use the three new letters to rotate the cubes. Whew!
Transformation Order Revisited
About the only thing left for this structure is to add scaling as well, but that would not add 
much to the OpenGL knowledge because the results are easy to predict. There is something 
else that can contribute significantly to the level of OpenGL understanding. The ability to 
switch back and forth between rotating before translation and rotating after translation 
reveals a dramatic difference between the two. Add the following to the case statement:

    'B': before := true;	
    'b': before := false;	
The form declaration needs to include the before field:
	
    errorCode: GLenum;	
    before,	
    openGLReady: boolean;	
    oldw,	
    oldh: integer;	
    x1,y1,z1,x2,y2,z2,xr1,yr1,zr1,	
    xr2,yr2,zr2,angle1,angle2,sign: glFloat;	
    procedure ExceptionGL(Sender:TObject;E:Exception);	
    procedure Cube;	
    procedure Axes;	
FormPaint needs an enhancement using the value of the before field to control whether the 
call to glRotatef happens before or after the call to glTranslatef:

  glPushMatrix;	
  if before then	
    glRotatef(angle1,xr1,yr1,zr1);	
  glTranslatef(x1,y1,z1);	
  if not before then	
    glRotatef(angle1,xr1,yr1,zr1);	
  Cube;	
  glPopMatrix;	
  glPushMatrix;	
  if before then	
    glRotatef(angle2,xr2,yr2,zr2);	
  glTranslatef(x2,y2,z2);       	


PAGE 75
   if not before then	
    glRotatef(angle2,xr2,yr2,zr2);	
  Cube;	
  glPopMatrix;	
Again save, compile, and run. Bring the cubes into view and position and orient them as you 
please, but be sure to include some rotation for each of them. Now press “B” and “b” to 
show the difference between rotation before translation and rotation after translation. The 
experiment with the order of transformations at the end of Chapter 3 gave small distinctions, 
but this time the difference is exceedingly obvious. For example, a small rotation about the y-
axis changes the direction of the cube’s own personal z-axis, so a subsequent translation 
along the z-axis sends it off in a different direction than if the translation happens first. Try it 
and see.
Another View
Frankly, transformations in OpenGL can be very confusing, so any aid to comprehension is 
welcome. For this purpose think of the translations as applying to the objects rather than the 
coordinate system. Then think of the transformations as happening in reverse order. When 
you push and pop the ModelView matrix several times, treat this reverse order concept 
separately between each push and pop.
Different people may need to consider OpenGL transformations in different ways to 
understand them. Possibly this alternate view will be the best one for many readers of this 
book.
Identifier Reference
Identifier			Definition
glFrustum
An OpenGL command that sets up perspective projection and the clipping volume.
glPopMatrix
An OpenGL command that saves the current OpenGL matrix on the matrix stack.
glPushMatrix
An OpenGL command that retrieves a matrix from the matrix stack and makes it the current 
OpenGL matrix.


PAGE 76
 Summary
Here are the high points of Chapter 4:
? The shape of the clipping region for perspective projection is a frustum, which is a 
pyramid with the top chopped off.
? The command is glFrustum; the parameters are the same as for glOrtho, but the znear 
and zfar parameters have different effects and must be positive.
? To move away from the viewer, deeper into the screen, is to move in the negative z 
direction.
? The effects of clipping and back face culling are the same as with glOrtho.
? When viewed in perspective, parallel lines appear to converge in the distance, and objects 
appear smaller as they retreat into the distance.
? Use glPushMatrix to save the ModelView matrix and glPopMatrix to restore it. Placing 
transformations between those calls allows for independent positioning and movement of 
objects in a scene.
? Rotation changes the orientation of the axes so that a subsequent translation may go off in 
a different direction.When planning a positioning or movement within a scene, be careful 
about the order of the transformations.
? OpenGL transformations seem to take effect in the reverse order of their specification in 
program code.

PAGE 77
 

PAGE 78

