About OpenGL Functions Used in This Tutorial

Transformations are carried out in OpenGL through matrix operations. OpenGL maintains three distinct stacks for storing matrices. Our interest here is in the model-view stack. We make this the current stack of interest with the function call

glMatrixMode(GL_MODELVIEW);

The call causes any subsequent matrix operations to be applied to this stack. The name implies the storage mechanism and the matrix on the top of the stack is called the current matrix. When we make the necessary call to perform, for example, a translation, the current matrix C on top of the stack is replaced by the matrix product CT, where T is the matrix for the translation. Next, when an object is drawn such as through calls to the glVertex method, each of the vertices v of the given geometry is multiplied by the current matrix (i.e., CTv) and the object with the transformed vertrices is drawn.

The view aspect of the model-view stack means that the viewing transformation matrix is also stored in this stack. The viewing transformation converts the definition of an object with coordinates given in world space to those defining the object in viewing space. The viewing transformation is established with a call to the function gluLookAt. This call creates the viewing transform matrix V and the product of the current matrix with this matrix (i.e., CV) becomes the current matrix on top of the model-view stack. In turn, the viewing matrix is ready to be applied to the geometry when drawing calls are made. Since the viewing transform matrix is multiplied by the current matrix, we generally want to initialize the current matrix to the identity matrix before making the call to gluLookAt. The sequence of function calls that we generally use to start the model-view stack are as follows:

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(eyeX, eyeY, eyeZ,
          centerX, centerY, centerZ,
          upX, upY, upZ);

where (eyeX, eyeY, eyeZ), (centerX, centerY, centerZ) and (upX, upY, upZ) are respectively the coordinates of the location of the virtual camera, the place were the camera is pointing (also called the look-at-point) and the roll of the camera established through an up vector. This is also discussed in the tutorial VirtualCamera.

Now that the viewing transformation is at the top of the model-view stack, we can proceed with including transformations at the top of the stack. This activity handles the modeling aspect of the rendering process. In particular, it allows us to manipulate the geometry in world space prior to transforming it to viewing coordinates and then to the screen. Let us start with translation. The OpenGL function call that we can make is

glTranslated(x, y, z);

where x, y and z are double values representing coordinates in 3-dimensional world space and the place where we want to move the object. A variation of the method is glTranslatef in which case the three coordinates are given as float values. With the call to glTranslate, the current matrix becomes the product of the viewing matrix and the translation matrix. Thus, when the current matrix VT is applied, objects are first moved in world space and then transformed to viewing space and last to the screen.

When working only in the x-y coordinate plane and 2-dimensional geometry such as in this tutorial, we set the value of z to 0.0. Also, the transformation matrices in OpenGL are 4-by-4. However, when working only in two dimensions, the third row and column of the matrices corresponding to the z-coordinate will always have the values 0.0, 0.0, 1.0 and 0.0. For this reason, we have shown in this tutorial only the 3-by-3 array of entries that vary.

Scaling in OpenGL is accomplished with the call

glScaled(x, y, z);

where x, y and z are double values and represent respectively the x, y and z scale factors. If the three values are equal, we are then applying a uniform scale; otherwise, the scaling is nonuniform. A variation of the method is glScalef in which case the parameters are given as float values. As with translation, the current matrix is replaced with the product of the current matrix and the scale matrix. Also in this tutorial the z value is fixed at 1.0 since we are working in 2-dimensional space.

Before looking at rotation, let us consider the order of method calls and its effect on the order in which tranformations are applied. We already realize the importance of the order. Seeing how OpenGL uses the current matrix at the top of the stack and updates it by the product of it and a transformation matrix, you should easily see that the last transformation method called is the first one to be applied to the geometry. This is easy to remember since it is very consistent with a stack storage, namely, "the last on is the first off." Therefore, if, for example, we want to apply a uniform scale by 2.0 followed by a translation to the point (1.0, 3.0, 0.0), the following is the sequence of OpenGL calls that we use.

glTranslated(1.0, 3.0, 0.0);
glScaled(2.0, 2.0, 2.0);

Since we start the model-view stack with the viewing transformation matrix V, the sequence of calls will produce VTS as the current matrix to be applied to the geometry.

Rotation in OpenGL is accomplished with the function

glRotated(angle, x, y, z);

where the four parameters are double values. Angle is the amount of rotation in degrees and it is done with respect to the vector (x, y, z). The vector provides a direction of the line about which rotation takes place. In particular, objects are rotated about the line going through the origin (0.0, 0.0, 0.0) and the point (x, y, z). In the case of working in 2-dimensional space and the X-Y coordinate plane, our rotations are in the X-Y plane and about the origin. Thus, the vector is fixed at (0.0, 0.0, 1.0), as done in this tutorial. A variation of the rotation function is glRotatef in which case the parameters are float values. Although the function establishes a rotation about a line going through the origin, we can still rotate about lines going through other points. We only need to translate the point to the origin, perform the rotation and then translate back to the original point. Suppose, for example, we want to rotate an object 90 degrees about a line parallel to the z-axis and going through the point (1.0, 2.0, 0.0). The following sequence of calls will accomplish this task.

glTranslated(1.0, 2.0, 0.0);
glRotated(90.0, 0.0, 0.0, 1.0);
glTranslated(-1.0, -2.0, 0.0);

One thing to keep in mind is rotations follow the right-hand rule. This means that if we place our eye so that the vector is pointing at us, then a rotation through a positive angle will appear as a counterclockwise rotation.

Our last consideration is performing multiple sequences of transformations and restoring the stack between each sequence. The OpenGL functions that we use to accomplish this are glPushMatrix and glPopMatrix. Each function is called without a parameter. Calling a push method without an object to push may seem strange as a stack operation. However, the OpenGL push method works differently on the model-view stack than a push method on a "normal" stack. In particular, with a call to glPushMatrix, the current matrix is pushed down one and is duplicated as the top element of the stack. In turn, the top element on the model-view stack and the one below it will be identical. The glPopMatrix method removes the top element, exposing the one below it. The two operations give us exactly what we need to start the model-view stack with the viewing transform V, perform, for example, a translation T and then restore the stack so that V is at the top. More specifically, suppose we have constructed a function called draw that defines the vertices of a square with one of the vertices at the origin. Suppose next that we want to draw two instances of the square, one at (1.0, 3.0, 0.0) and another at (15.0, 25.0, 0.0). Assuming that we have already established the viewing transform matrix on top of the stack, the following sequence of OpenGL calls will accomplish our task.

glPushMatrix();
glTranslated(1.0, 3.0, 0.0);
draw();
gl.PopMatrix();
 
glPushMatrix();
glTranslated(15.0, 25.0, 0.0);
draw();
PopMatrix();

And now "for the rest of the story." The OpenGL functions dicussed so far will be used in most of our applications that involve transformations. But keep in mind that the transform matrices corresponding to the OpenGL translate, scale and rotate functions are multiplied on the RIGHT of the current matrix and are applied in the order "last on, first applied." However, there are times when we need to construct transformations and apply them in the order they are determined -- that is, last determined is the last to be applied. That is the case in this tutorial where the sequence of transformations is determined through user interaction. Our solution is not to use the translate, scale and rotate methods but instead construct the 4-by-4 composite transformation matrix and have it added to the model-view stack. The latter is accomplished with the function call

glMultMatrixd(M);

where M is a one-dimensional array of 16 double values. Futher, M stores our 4-by-4 composite transformation matrix in COLUMN MAJOR format. This means that the first four entries of M are the entries of the first column of our transformation matrix, the next four entries of M are from the next column and so forth. Last, the current matrix C on the model-view stack is replaced by the product of C times the 4-by-4 matrix that is stored in M.