OpenGL Viewpoint and Object Transformations
For those of you that don't know, transformation in 3D programming refers to altering the position and rotation of the rendering volume in a given 3D API so that objects are rendered with the correct orientation.
I have seen many demos in OpenGL articles displaying a rotating cube, or even a complicated particle system. But very rarely have I seen demos depicting many objects each with a different orientation. This page will give you some pointers on how to accomplish this, and maybe even give you some nice classes to get started with.
I'll be using some phrases that might not be standard to 3D rendering academia, so if you find that my terminologies don't quite mesh with other articles you've read, I hope it doesn't cause you too much confusion. Most of my 3D programming is self taught, so I'm not entirely aware of the vocabulary that the "rest" of the industry is used to.
Common Data Structures
Two structures are needed for this demonstration: a vector class and an orientation class.
class CVector3D {
float x, y, z;
};
class COrient3D {
CVector3D l, u, f;
CVector3D p;
};
To clarify, l stands for left, u for up, and f for forward.
p stands for position, and is just the position in 3D space.
l, u, and f are the orientation vectors, and have the following properties:
Generally, l, u and f start out with the following values:
And it is VITAL that l, u and f maintain the properties mentioned above after any rotation or calculation you perform on them!!! l, u and f in essence represent the facing that an object has in 3D space (l, u and f represent the object's left, up and forward directions, respectively)
I won't worry about supplying constructor, destructor or any other functions with these definitions. If you're serious about 3D programming, you are hopefully already aware of the various vector functions (scalar multiplication, magnitude, dot product, cross product, addition, subtraction, etc.), and you can imagine the kinds of functions you will need for the orientation class (movement along an axis, movement along a vector, rotation along an axis, and the hardest one, rotation along an arbitrary vector).
Transformations
Two types of transformations are performed using the COrient3D class: What I will call forward transformations and inverse transformations. Forward transformations are used to render normal 3D objects in the 3D world. One inverse transformation is used to initially set up the viewpoint.
The basic algorithm for rendering different objects with their own orientation goes as follows:
The transformations themselves are performed by loading up a 4x4 matrix and calling glMultMatrix to affect the viewing volume.
A 4x4 matrix in C++ is basically a 16-element array with the following arrangement of indices:
0 | 4 | 8 | 12 |
1 | 5 | 9 | 13 |
2 | 6 | 10 | 14 |
3 | 7 | 11 | 15 |
To perform an inverse transformation, do the following:
matrix =
lx | ly | lz | 0 |
ux | uy | uz | 0 |
fx | fy | fz | 0 |
0 | 0 | 0 | 1 |
call glMultMatrix with this matrix, then call glTranslatef(-px, -py, -pz); to complete the inverse transformation.
A forward transformation works this way:
matrix =
lx | ux | fx | px |
ly | uy | fy | py |
lz | uz | fz | pz |
0 | 0 | 0 | 1 |
A single call to glMultMatrix takes care of this.
PLEASE NOTE: This is all with the assumption that whatever model you are rendering after the transformation is defined with its center at (0.0f, 0.0f, 0.0f). For example, the cube with vertices defined at (+/-1.0f, +/-1.0f, +/-1.0f) will work because its center is ( 0.0f, 0.0f, 0.0f). If the center of the object does NOT lie on zeroes, then the model will orbit a point outside of itself as you modify the structure representing its orientation. If you have such an object but you want to make it appear as though it is rotating naturally, you will need to call glTranslate(-x, -y, -z), where (x, y, z) is the center of the model.
The Benefits
What has really helped about representing the orientations this way is that it is very intuitive, at least for me. If I want to see how one object exists in relation to another object's orientation, all I need to do is get the vector from one object's p to the other object's p, and perform a dot product against the l, u or f axes as appropriate. This helps me determine, for example, how one object might need to alter its orientation to face another, which would be good for jets in a dogfight.
Another great benefit is that you can go hog wild setting every orientation exactly as you wish (just make sure you maintain the cross products and magnitude of the l, u and f axes), and the algorithm I mentioned above will render every object for you exactly as it needs to be.
You can even assign one object to actually BE the viewpoint, and reassign the viewpoint to any orientation object you want without having to perform any special recalculations.
These structures have served me very well!