Viewing and Modeling Transformations (OpenGL Programming) Part 2

A Modeling Transformation Code Example

Example 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 O-axis) increased by 50 percent.

•    A rotated triangle, made of dotted lines, is drawn.

Modeling Transformation Example

Figure 3-8 Modeling Transformation Example

Example 3-2 Using Modeling Transformations: model.c

Using Modeling Transformations: model.c


Note the use of glLoadldentityO to isolate the effects of modeling transformations; initializing the matrix values prevents successive transformations from having a cumulative effect. Even though using glLoadldentityO repeatedly has the desired effect, it may be inefficient, because you may have to respecify viewing or modeling transformations. (See "Manipulating the Matrix Stacks" on page 145 for a better way to isolate transformations.)

Note: Sometimes, programmers who want a continuously rotating object attempt to achieve this by repeatedly applying a rotation matrix that has small values. The problem with this technique is that because of round-off errors, the product of thousands of tiny rotations gradually drifts away from the value you really want (it might even become something that isn’t a rotation). Instead of using this technique, increment the angle and issue a new rotation command with the new angle at each update step.

Nate Robins’ Transformation Tutorial

If you have downloaded Nate Robins’ suite of tutorial programs, this is an opportune time to run the transformation tutorial. (For information on how and where to download these programs, see "Nate Robins’ OpenGL Tutors" on page xxiv.) With this tutorial, you can experiment with the effects of rotation, translation, and scaling.

Viewing Transformations

A 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 z-axis.

•    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 to encapsulate 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), you generally 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 z-axis. (You’re seeing the back of the camera.)

Object and Viewpoint at the Origin

Figure 3-9 Object and Viewpoint at the Origin

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 z-axis; if you rotate the viewpoint, forward has a different meaning. Therefore, to put five units of distance between the viewpoint and the objects by moving the viewpoint, as shown in Figure 3-10, use

tmp5324-169_thumb[2]

 

 

Separating the Viewpoint and the Object

Figure 3-10 Separating the Viewpoint and the Object

This routine moves the objects in the scene -5 units along the z-axis. This is also equivalent to moving the camera +5 units along the z-axis.

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. You know that with the fixed coordinate system approach, commands have to be issued in the opposite order in which they should take effect, so you know that you need to write the translate command in your code first 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

tmp5324-171_thumb[2]

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 gluLookAt() utility routine described next.

Using the glul_ookAt() Utility Routine

Often, programmers construct a scene around the origin or some other convenient location and then want to look at it from an arbitrary point to get a good view of it. As its name suggests, the gluLookAt() utility routine is designed for just this purpose. It takes three sets of arguments, which specify the location of the viewpoint, define a reference point toward which the camera is aimed, and indicate which direction is up. Choose the viewpoint to yield the desired view of the scene. The reference point is typically somewhere in the middle of the scene. (If you’ve built your scene at the origin, the reference point is probably the origin.) It might be a little trickier to specify the correct up-vector. Again, if you’ve built some real-world scene at or around the origin and if you’ve been taking the positive y-axis to point upward, then that’s your up-vector for gluLookAt(). However, if you’re designing a flight simulator, up is the direction perpendicular to the plane’s wings, from the plane toward the sky when the plane is right-side-up on the ground.

The gluLookAt() routine is particularly useful when you want to pan across a landscape, for instance. With a viewing volume that’s symmetric in both x and y, the (eyex, eyey, eyez) point specified is always in the center of the image on the screen, so you can use a series of commands to move this point slightly, thereby panning across the scene.

void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble center}’, GLdouble ccnterz, GLdouble upx, GLdouble upy, GLdouble upz);

Defines a viewing matrix and multiplies it to the right of the current matrix. The desired viewpoint is specified by eyex, eyey, and eyez. The centerx, centery, and centerz arguments specify any point along the desired line of sight, but typically they specify some point in the center of the scene being looked at. The upx, upy, and upz arguments indicate which direction is up (that is, the direction from the bottom to the top of the viewing volume).

In the default position, the camera is at the origin, is looking down the negative z-axis, and has the positive y-axis as straight up. This is the same as calling

tmp5324-172_thumb[2]

The z-value of the reference point is -100.0, but could be any negative z, because the line of sight will remain the same. In this case, you don’t actually want to call gluLookAt(), because this is the default (see Figure 3-11) and you are already there. (The lines extending from the camera represent the viewing volume, which indicates its field of view.)

Default Camera Position

Figure 3-11 Default Camera Position

Figure 3-12 shows the effect of a typical gluLookAt() routine. The camera position (eyex, eyey, eyez) is at (4, 2, 1). In this case, the camera is looking right at the model, so the reference point is at (2, 4, -3). An orientation vector of (2, 2, -1) is chosen to rotate the viewpoint to this 45-degree angle.

 Using gluLookAt() Therefore, to achieve this effect, call

Figure 3-12 Using gluLookAt() Therefore, to achieve this effect, call

tmp5324-175_thumb[2]

Note that gluLookAt() is part of the Utility Library, rather than the basic OpenGL library. This isn’t because it’s not useful, but because it encapsulates several basic OpenGL commands—specifically, glTranslate*() and glRotate*(). To see this, imagine a camera located at an arbitrary viewpoint and oriented according to a line of sight, both as specified with gluLookAt() and a scene located at the origin. To "undo" what gluLookAt() does, you need to transform the camera so that it sits at the origin and points down the negative z-axis, the default position. A simple translate moves the camera to the origin. You can easily imagine a series of rotations about each of the three axes of a fixed coordinate system that would orient the camera so that it pointed toward negative z-values. Since OpenGL allows rotation about an arbitrary axis, you can accomplish any desired rotation of the camera with a single glRotate*() command.

Note: You can have only one active viewing transformation. You cannot try to combine the effects of two viewing transformations, any more than a camera can have two tripods. If you want to change the position of the camera, make sure you call glLoadldentityO to erase the effects of any current viewing transformation.

Nate Robins’ Projection Tutorial

If you have Nate Robins’ suite of tutorial programs, run the projection tutorial. With this tutorial, you can see the effects of changes to the parameters of gluLookAt().

Advanced

To transform any arbitrary vector so that it’s coincident with another arbitrary vector (for instance, the negative z-axis), you need to do a little mathematics. The axis about which you want to rotate is given by the cross product of the two normalized vectors. To find the angle of rotation, normalize the initial two vectors. The cosine of the desired angle between the vectors is equal to the dot product of the normalized vectors. The angle of rotation around the axis given by the cross product is always between 0 and 180 degrees.

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 Advanced

For some specialized applications, you might want to define your own transformation routine. Since this is rarely done and 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.

Try This

• 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:

tmp5324-176_thumb[2]

• 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 xy-plane, measured from the positive y-axis. Similarly, elevation is the angle of rotation of the camera in the yz-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:

tmp5324-177_thumb[2]

Next post:

Previous post: