The OpenGL Viewing Pipeline Explained
The OpenGL Viewing Pipeline Explained
In the subsequent sections we will examine how we can define each of these matrices using
C++/OpenGL code. Remember that OpenGL will postmultiply any matrices we specify by the
current matrix (i.e. the one on top of the matrix stack). This means that if we want to specify a
sequence of transformations for any of the viewing pipeline matrices, we must define them in
reverse order.
3.1 The Modelview Matrix
We mentioned in the last chapter that OpenGL maintains a matrix stack that we can use to store
matrices that we may need later. In fact, OpenGL maintains one matrix stack for each matrix in
the viewing pipeline. In other words, there is one modelview matrix stack, one projection matrix
stack and one viewport matrix stack. Therefore, before making any modifications to our current
matrix (the top one on the stack) we must tell OpenGL which matrix we are talking about. The
following line of code specifies to OpenGL that any subsequent transformations that we specify
will apply to the modelview matrix:
glMatrixMode(GL_MODELVIEW);
Now we can use any of the following OpenGL functions to modify the current modelview matrix:
glTranslate*
glRotate*
glScale*
glLoadMatrix*
glMultMatrix*
gluLookAt
The first five of these we have already seen in the last chapter. These are typically used to define
the modelling part of the modelview matrix. That is, they are used to transform the primitives to
form our 3-D scene in world coordinates. However, the last, gluLookAt, needs further explanation.
This function is used to define the viewing part of the transformation. Recall that the viewing
transformation defines the position, direction and orientation of a virtual camera. The basic format
of gluLookAt is as follows:
The first three arguments define a position (x0, y0, z0) in 3-D world coordinates. This represents
the position of the virtual camera. The next three arguments define another position in 3-D world
coordinates: (xref, yref, zref). This is the position that the camera is ‘looking’ at. For example, if
we specified (0,0,-10) at the camera position and (0,0,0) as the ‘look’ position, our virtual camera
would be looking along the positive z-axis. Finally we have the last three arguments (Vx, Vy, Vz).
These three values form a 3-D ‘up’ vector that defines the orientation of the camera. If the vector
is not perpendicular to the ‘look’ direction then it is projected so that it is perpendicular.
For example, the following 3 lines define a virtual camera positioned at (8, 0, 8) in world
coordinates, looking at the origin, and with the y-axis as ‘up’.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(8,0,8,0,0,0,0,1,0);
Note that we have initialised the current modelview matrix to the identity matrix before defining
the viewing matrix. It is always necessary to do this, because OpenGL matrix functions
postmultiply by the current matrix, so if this current matrix is not initialised the results will be
undefined.
If you do not specify a viewing matrix in OpenGL the default parameters are:
(x0, y0, z0) = (0, 0, 0)
(xref, yref, zref) = (0, 0, -1)
(Vx, Vy, Vz) = (0, 1, 0)
That is, the camera is positioned at the origin, pointing along the negative z-axis, with the y-axis
as ‘up’. For many applications this may be sufficient, so sometimes OpenGL programs will not
define a viewing matrix.
Recall from Error! Reference source not found. that the viewing transformation occurs after the
modelling transformation. However, because OpenGL postmultiplies all matrices, it must be
specified before the modelling transformation. The viewing transformation, if specified, will
normally be common to all primitives in the scene. However, the modelling transformation is
normally different for each primitive. Therefore, it is normal to define a single viewing
transformation to begin with, and then to modify it with individual modelling transformations for
each primitive. But because we only have a single current matrix, whenever we define modelling
transformations for a given primitive it will overwrite the common viewing transformation, which
we need to keep for subsequent primitives. How can we ‘remember’ this viewing transformation
so that we don’t need to redefine it for each primitive?
The answer to this question is to use the matrix stack. We can remember the viewing
transformation by pushing it onto the modelview matrix stack, and then when we need it for the
next primitive, we pop it back again. The standard sequence of events is therefore:
Define the viewing matrix
For each primitive we want to draw:
o Push the viewing matrix onto the stack
o Postmultiply the viewing matrix by the modelling transformations for this primitive
o Draw the primitive(s)
o Pop the viewing matrix back from the stack
For example, the code example shown below displays two primitives (both 2-D rectangles) using
the same viewing transformation but different modelling transformations.
// initialize modelview matrix to identity
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// draw primitives
glPushMatrix(); // remember viewing matrix
glRotatef(45,0,0,1); //rotate this primitive
glRecti(0,0,50,50); // draw rectangle
glPopMatrix(); // restore viewing matrix
Depending on the type of projection required, OpenGL provides 4 alternative functions for
specifying the projection matrix:
gluOrtho2D
glOrtho
gluPerspective
glFrustum
gluOrtho2D
OpenGL provides two functions for specifying orthographic parallel projections (it does not
support oblique projections). The gluOrtho2D function is intended for use with 2-D graphics only,
as it does not allow you to specify near and far clipping planes. The basic format of gluOrtho2D
is:
The four arguments specify the x-coordinates of the left and right clipping planes, and the y-
coordinates of the bottom and top clipping planes. All coordinates are specified in the viewing
coordinate system (i.e. the virtual camera is at the origin and looking along the negative z-axis,
and the y-axis is ‘up’). In 2-D graphics we just ignore the third coordinate (it is normally set to
zero), so orthographic projections are ideal and no near and far clipping planes are required.
For example, the lines of code shown below define a 2-D orthographic projection that will display
all primitives with x-coordinate between 0 and 640, and y-coordinate between 0 and 480. Note
that, just as with the modelview matrix, the first thing we have to do it tell OpenGL which matrix
we are referring to, and initialise it to the identity matrix.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 480.0);
glOrtho
An alternative function for defining orthographic projections is glOrtho. This is very similar to
gluOrtho2D except that it allows us to specify near and far clipping planes, in addition to the left,
right, bottom and top ones. The basic format is:
Here the six arguments represent the x-coordinates of the left and right clipping planes, the y-
coordinates of the bottom and top clipping planes, and the z-coordinates of the near and far clipping
planes. Again, all coordinate are specified in the viewing coordinate system.
The following code specifies an orthographic projection that defines a view volume that is a
24x24x30 cuboid. The left and right bounds of this view volume are at x=-12 and x=+12, the
bottom and top bounds are at y=-12 and y=+12, the near clipping plane is at the virtual camera
position (z=0) and the far clipping plane is 30 units away from the camera in the viewing direction.
Since the viewing direction of the viewing coordinate system is the negative z-axis in OpenGL,
this means that the far clipping plane is positioned at z=-30 in this example.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// arguments are left, right, bottom, top, near, far
glOrtho(-12, 12, -12, 12, 0, 30);
gluPerspective
If a perspective projection is required, OpenGL provides two alternative functions. The first is
gluPerspective. The basic format is:
Here, theta represents the height angle of the perspective projection. The height angle is the
angle between the top and bottom bounds of the image (see Error! Reference source not found.).
The next argument, aspect, is the aspect ratio of the image (the ratio of the width to the height).
If OpenGL knows the height angle and the aspect ratio it is straightforward to determine the width
angle. The last two arguments, dnear and dfar, are the z-coordinates of the near and far clipping
planes.
As an example, the following code defines a perspective projection with a height angle of 45o. The
image has an aspect ratio of 1.0 – this means that the width of the image is the same as the height,
and therefore the width angle must also be 45o. The near clipping plane is positioned at z=0, or the
origin of the viewing coordinate system, and the far clipping plane is at a distance of 50 from the
camera. For same reasons given above, this positions it at z=-50.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, 1.0, 0.0, 50.0);
glFrustum
Recall that perspective projections lead to a view volume in the shape of a frustum. The final
OpenGL perspective projection routine allows you to specify the vertices of this frustum directly.
The basic format is:
Here, the first 4 arguments are the x and y coordinates of the left, right, bottom and top clipping
planes at the near clipping plane. The last two arguments, dnear and dfar, represent the z-
coordinates of the near and far clipping planes. Therefore the positions of the four corners of the
near clipping plane are (in viewing coordinates):
(xwmin, ywmin, dnear)
(xwmin, ywmax, dnear)
(xwmax, ywmin, dnear)
(xwmax, ywmax, dnear)
The positions of the corners of the far clipping plane can be determined from these coordinates,
the centre of projection (which is the origin) and the value of dfar.
For example, the code example given below specifies a perspective projection in which the near
clipping plane is defined by the four corners (-10, -10, -5), (-10, 10, -5), (10, -10, -5) and (10, 10,
-5). Note that the z-coordinates are negated. The dnear argument specifies the distance of the
near clipping plane from the virtual camera, and the virtual camera looks along the negative z-axis
of the viewing coordinate system. The far clipping plane is at a distance 50 from the camera so it
has a z-coordinate of -50.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluFrustum(-10,10,-10,10,5,50);
3.3 The Viewport Matrix
The viewport matrix is defined slightly differently to the modelview and projection matrices. There
is only one function for defining the viewport matrix so we do not need to use glMatrixMode to
tell OpenGL which matrix we are referring to – it is unambiguous. The function is glViewport and
its basic format is:
Referring to Error! Reference source not found., (xmin, ymin) is the bottom-left corner of the
viewport, in the coordinate system of the screen window (whose origin is at the bottom-left). The
arguments xsize and ysize are the size of the viewport in pixels.
For example, assuming that we have a screen window of size 640x480, the following line defines
the viewport to be of size 300x300 and centred in the screen window.