## Viewing and Modeling TransformationsViewing and modeling transformations are inextricably related
in OpenGL and are in fact combined into a single modelview matrix. (See "A Simple Example: Drawing a Cube.")
One of the toughest problems newcomers to computer graphics face is understanding
the effects of combined three-dimensional transformations. As you've already
seen, there are alternative ways to think about transformations - do you want
to move the camera in one direction, or move the object in the opposite direction?
Each way of thinking about transformations has advantages and disadvantages,
but in some cases one way more naturally matches the effect of the intended
transformation. If you can find a natural approach for your particular application,
it's easier to visualize the necessary transformations and then write the corresponding
code to specify the matrix manipulations. The first part of this section discusses
how to think about transformations; later, specific commands are presented.
For now, we use only the matrix-manipulation commands you've already seen. Finally,
keep in mind that you must call ## Thinking about TransformationsLet's start with a simple case of two transformations: a 45-degree
counterclockwise rotation about the origin around the
Now let's talk about the order in which you specify a series
of transformations. All viewing and modeling transformations are represented
as 4 × 4 matrices. Each successive Consider the following code sequence, which draws a single point using three transformations: `glMatrixMode(GL_MODELVIEW);`
`glLoadIdentity();`
glMultMatrixf(N); /* apply transformation N */ glMultMatrixf(M); /* apply transformation M */ glMultMatrixf(L); /* apply transformation L */ `glBegin(GL_POINTS);`
glVertex3f(v); /* draw transformed vertex v */ `glEnd();`
With this code, the modelview matrix successively contains
## Grand, Fixed Coordinate SystemThus, if you like to think in terms of a grand, fixed coordinate
system - in which matrix multiplications affect the position, orientation, and
scaling of your model - you have to think of the multiplications as occurring
in the opposite order from how they appear in the code. Using the simple example
shown on the left side of Figure 3-4 (a rotation about the origin and a translation along the `glMatrixMode(GL_MODELVIEW);`
`glLoadIdentity();`
glMultMatrixf(T); /* translation */ glMultMatrixf(R); /* rotation */ `draw_the_object();`
## Moving a Local Coordinate SystemAnother way to view matrix multiplications is to forget about
a grand, fixed coordinate system in which your model is transformed and instead
imagine that a local coordinate system is tied to the object you're drawing.
All operations occur relative to this changing coordinate system. With this
approach, the matrix multiplications now appear in the natural order in the
code. (Regardless of which analogy you're using, the code is the same, but how
you think about it differs.) To see this in the translation-rotation example,
begin by visualizing the object with a coordinate system tied to it. The translation
operation moves the object and its coordinate system down the This approach is what you should use for applications such as articulated robot arms, where there are joints at the shoulder, elbow, and wrist, and on each of the fingers. To figure out where the tips of the fingers go relative to the body, you'd like to start at the shoulder, go down to the wrist, and so on, applying the appropriate rotations and translations at each joint. Thinking about it in reverse would be far more confusing. This second approach can be problematic, however, in cases where scaling occurs, and especially so when the scaling is nonuniform (scaling different amounts along the different axes). After uniform scaling, translations move a vertex by a multiple of what they did before, since the coordinate system is stretched. Nonuniform scaling mixed with rotations may make the axes of the local coordinate system nonperpendicular. As mentioned earlier, you normally issue viewing transformation
commands in your program before any modeling transformations. This way, a vertex
in a model is first transformed into the desired orientation and then transformed
by the viewing operation. Since the matrix multiplications must be specified
in reverse order, the viewing commands need to come first. Note, however, that
you don't need to specify either viewing or modeling transformations if you're
satisfied with the default conditions. If there's no viewing transformation,
the "camera" is left in the default position at the origin, pointed
toward the negative Since the commands for performing modeling transformations
can be used to perform viewing transformations, modeling transformations are
## Modeling TransformationsThe three OpenGL routines for modeling transformations are
In the command summaries that follow, each matrix multiplication is described in terms of what it does to the vertices of a geometric object using the fixed coordinate system approach, and in terms of what it does to the local coordinate system that's attached to an object. ## Translate
Figure 3-5 shows the effect of
Note that using (0.0, 0.0, 0.0) as the argument for ## Rotate
The effect of
Note that an object that lies farther from the axis of rotation
is more dramatically rotated (has a larger orbit) than an object drawn near
the axis. Also, if the ## Scale
Figure 3-7 shows the effect of
## A Modeling Transformation Code ExampleExample 3-2 is a portion of a program that renders a triangle four times, as shown in Figure 3-8. These are the four transformed triangles. - A solid wireframe triangle is drawn with no modeling transformation.
- The same triangle is drawn again, but with a dashed line stipple and translated (to the left - along the negative x-axis).
- A triangle is drawn with a long dashed line stipple, with its height (
`y`-axis) halved and its width (`x`-axis) increased by 50%. - A rotated triangle, made of dotted lines, is drawn.
glLoadIdentity(); glColor3f(1.0, 1.0, 1.0); draw_triangle(); /* solid lines */ glEnable(GL_LINE_STIPPLE); /* dashed lines */ glLineStipple(1, 0xF0F0); glLoadIdentity(); glTranslatef(-20.0, 0.0, 0.0); draw_triangle(); glLineStipple(1, 0xF00F); /*long dashed lines */ glLoadIdentity(); glScalef(1.5, 0.5, 1.0); draw_triangle(); glLineStipple(1, 0x8888); /* dotted lines */ glLoadIdentity(); glRotatef (90.0, 0.0, 0.0, 1.0); draw_triangle (); glDisable (GL_LINE_STIPPLE); Note the use of
## Viewing TransformationsA viewing transformation changes the position and orientation of the viewpoint. If you recall the camera analogy, the viewing transformation positions the camera tripod, pointing the camera toward the model. Just as you move the camera to some position and rotate it until it points in the desired direction, viewing transformations are generally composed of translations and rotations. Also remember that to achieve a certain scene composition in the final image or photograph, you can either move the camera or move all the objects in the opposite direction. Thus, a modeling transformation that rotates an object counterclockwise is equivalent to a viewing transformation that rotates the camera clockwise, for example. Finally, keep in mind that the viewing transformation commands must be called before any modeling transformations are performed, so that the modeling transformations take effect on the objects first. You can manufacture a viewing transformation in any of several ways, as described
next. You can also choose to use the default location and orientation of the
viewpoint, which is at the origin, looking down the negative - Use one or more modeling transformation commands (that is,
**glTranslate*()**and**glRotate*()**). You can think of the effect of these transformations as moving the camera position or as moving all the objects in the world, relative to a stationary camera. - Use the Utility Library routine
**gluLookAt()**to define a line of sight. This routine encapsulates a series of rotation and translation commands. - Create your own utility routine that encapsulates rotations and translations. Some applications might require custom routines that allow you to specify the viewing transformation in a convenient way. For example, you might want to specify the roll, pitch, and heading rotation angles of a plane in flight, or you might want to specify a transformation in terms of polar coordinates for a camera that's orbiting around an object.
## Using glTranslate*() and glRotate*()When you use modeling transformation commands to emulate viewing transformations,
you're trying to move the viewpoint in a desired way while keeping the objects
in the world stationary. Since the viewpoint is initially located at the origin
and since objects are often most easily constructed there as well (see Figure 3-9), in general you have to perform some transformation
so that the objects can be viewed. Note that, as shown in the figure, the camera
initially points down the negative
In the simplest case, you can move the viewpoint backward, away from the objects;
this has the same effect as moving the objects forward, or away from the viewpoint.
Remember that by default forward is down the negative glTranslatef(0.0, 0.0, -5.0); This routine moves the objects in the scene -5 units along the
Now suppose you want to view the objects from the side. Should you issue a rotate command before or after the translate command? If you're thinking in terms of a grand, fixed coordinate system, first imagine both the object and the camera at the origin. You could rotate the object first and then move it away from the camera so that the desired side is visible. Since you know that with the fixed coordinate system approach, commands have to be issued in the opposite order in which they should take effect, you know that you need to write the translate command first in your code and follow it with the rotate command. Now let's use the local coordinate system approach. In this case, think about moving the object and its local coordinate system away from the origin; then, the rotate command is carried out using the now-translated coordinate system. With this approach, commands are issued in the order in which they're applied, so once again the translate command comes first. Thus, the sequence of transformation commands to produce the desired result is glTranslatef(0.0, 0.0, -5.0); glRotatef(90.0, 0.0, 1.0, 0.0); If you're having trouble keeping track of the effect of successive matrix multiplications,
try using both the fixed and local coordinate system approaches and see whether
one makes more sense to you. Note that with the fixed coordinate system, rotations
always occur about the grand origin, whereas with the local coordinate system,
rotations occur about the origin of the local system. You might also try using
the ## Using the gluLookAt() Utility RoutineOften, programmers construct a scene around the origin or some other convenient
location, then they want to look at it from an arbitrary point to get a good
view of it. As its name suggests, the The
In the default position, the camera is at the origin, is looking down the negative
gluLookat (0.0, 0.0, 0.0, 0.0, 0.0, -100.0, 0.0, 1.0, 0.0); The
Figure 3-12 shows the effect of a typical
So, to achieve this effect, call gluLookAt(4.0, 2.0, 1.0, 2.0, 4.0, -3.0, 2.0, 2.0, -1.0); Note that
To transform any arbitrary vector so that it's coincident with another arbitrary
vector (for instance, the negative Note that computing the angle between two normalized vectors by taking the inverse cosine of their dot product is not very accurate, especially for small angles. But it should work well enough to get you started. ## Creating a Custom Utility Routine
For some specialized applications, you might want to define your own transformation routine. Since this is rarely done and in any case is a fairly advanced topic, it's left mostly as an exercise for the reader. The following exercises suggest two custom viewing transformations that might be useful.
- Suppose you're writing a flight simulator and you'd like to
display the world from the point of view of the pilot of a plane. The world
is described in a coordinate system with the origin on the runway and the
plane at coordinates (
`x, y, z`). Suppose further that the plane has some`roll`,`pitch`, and`heading`(these are rotation angles of the plane relative to its center of gravity).
Show that the following routine could serve as the viewing transformation: void pilotView{GLdouble planex, GLdouble planey, GLdouble planez, GLdouble roll, GLdouble pitch, GLdouble heading) { glRotated(roll, 0.0, 0.0, 1.0); glRotated(pitch, 0.0, 1.0, 0.0); glRotated(heading, 1.0, 0.0, 0.0); glTranslated(-planex, -planey, -planez); } - Suppose your application involves orbiting the camera around
an object that's centered at the origin. In this case, you'd like to specify
the viewing transformation by using polar coordinates. Let the
`distance`variable define the radius of the orbit, or how far the camera is from the origin. (Initially, the camera is moved`distance`units along the positive`z`-axis.) The`azimuth`describes the angle of rotation of the camera about the object in the`x-y`plane, measured from the positive`y`-axis. Similarly,`elevation`is the angle of rotation of the camera in the`y-z`plane, measured from the positive`z`-axis. Finally,`twist`represents the rotation of the viewing volume around its line of sight.
Show that the following routine could serve as the viewing transformation: void polarView{GLdouble distance, GLdouble twist, GLdouble elevation, GLdouble azimuth) { glTranslated(0.0, 0.0, -distance); glRotated(-twist, 0.0, 0.0, 1.0); glRotated(-elevation, 1.0, 0.0, 0.0); glRotated(azimuth, 0.0, 0.0, 1.0); } |