# CS405 Lab 3: Matrix Transformation

## Highlights of this lab:

This lab is an introduction to Matrix Transformation

• Overview of elementary transformation: Rotation, Translation, and Scaling Transformation
• OpenGL's modelview matrix introduction: Modeling transformations, Viewing transformations, Components of Modelview Matrix, Manipulating the matrix directly, Viewport transformations, Projection transformations

## Assignment

After the lab lecture, you have approximately two weeks of time to:

• make some modifications to "main.cpp" and answer some questions
• Create the fingers for a robot arm

## Lecture Notes

### A. A Simplified View of the OpenGL Pipeline

Each vertex of polygons will pass through two main stages of transformations:

• Model view transformation (translation, rotation, and scaling of objects, 3D viewing transformation)
• Projection (perspective or orthographic)

There is one global matrix internally for each of the two stage above:

• Mmodelview
• Mprojection

Given a 3D vertex of a polygon, P = [x, y, z, 1]T, in homogeneous coordinates, applying the model view transformation matrix to it will yield a new coordinate:

P’ = [x’, y’, z’, 1]T = Mmodelview*P.

By applying projection to P’, a 2D coordinate in homogeneous form is produced:

P” = [x”, y”, 1]T = Mprojection*P’.

The final coordinate [x”, y”] will be a point on the screen to be drawn.

Each matrix can be, and should be, initialized to identity at the beginning of an application.

For example, setting the model view matrix to the identity matrix can be done by:

```glMatrixMode(GL_MODELVIEW); //selecting the global matrix to be modified
glLoadIdentity();           //set the currently selected global matrix to identity```

Similarly, we can set the projection matrix to identity as well:

```glMatrixMode(GL_PROJECTION);

These two global transformation matrices can be modified by the following elementary transformation functions as required by individual applications.

### B. Elementary Transformation

• Right-handed and left-handed coordinate system: If you take your thumb and the first two fingers of your right hand and hold them out at angles to each other, the arrangement is similar to the one shown for the right handed system. The other system shown is a left-handed coordinate system and is sometimes used in graphics text. A consequence of using the right-handed system is that the negative z-axis goes into the screen.
• Right-handed coordinate system is used most often. In OpenGL, both the local coordinate system for object models (such as cube, sphere), and the camera coordinate system are defined in right-handed system.
• In the following discussion, we assume that the matrix mode is set to GL_MODELVIEW. Therefore, all transformation function calls apply to Mmodelview, unless the other is specifically mentioned.

#### Translation:

`glTranslate*(dx, dy, dz);`

where [dx, dy, dz] is the translation vector.

The effect of calling this function is to concatenate the translation matrix defined by the parameters [dx, dy, dz] to the global model view matrix:

Mmodelview = Mmodelview * T(dx, dy, dz);

Where T(dx, dy, dz) =

In general, a new transformation matrix is always concatenated to the global matrix from the right. This is often called post-multiplication.

#### Rotation:

`glRotate*(angle, x, y, z)`

Where angle is the angle of counterclockwise rotation in degrees, and x, y and z define a vector(originating at the origin) to rotate about.

Typically we rotate about only one of the major axes, which simplifies x, y and z to be a unit vector.

For example, if we want to rotate about the x-axis, then x=1, y=0, and z=0.

The effect of calling a rotation matrix is similar to translation. For example, the function call:

`glRotatef(a, 1, 0, 0);`

will have the following effect:

Mmodelview = Mmodelview * Rx(a);

Where Rx(a) denote the rotation matrix about the x-axis for degree a: Rx(a) =

Rotations about the y-axis or z-axis can be achieved respectively by functions calls:

```glRotatef(a, 0, 1, 0); // rotation about the y-axis
glRotatef(a, 0, 0, 1); // rotation about the z-axis```

#### Scaling

`glScale*(sx, sy, sz);`

where sx, sy and sz are the scaling factors along each axis with respect to the local coordinate system of the model. The scaling transformation allows a transformation matrix to change the dimensions of an object by shrinking or stretching along the major axes centered on the origin.

Example: to make the wire cube three times as high, we can stretch it along the y-axis by a factor of 3 by using the following commands.

```     // make the y dimension 3 times larger
glScalef(1, 3, 1);
// draw the cube
glutWireCube(1); ```
• It should be noted that the scaling is always about the origin along each dimension with the respective scaling factors.
• The effect of calling this function to the global model view matrix is similar to translation and rotation.

### C. The Order of Transformations

• In OpenGL, the order in which the transformations are applied is the opposite of the order in which they appear in the program. In order words, the last transformation specified is the first one applied. This is due to the post-multiplication described above. This property can be best illustrated by the following examples.
• The initial default position for the camera is at the origin, and the lens is looking into the negative z direction.
• The primitive object models provided by OpenGL, such as cube or sphere, are also defined at the origin with a unit size by default.
• The purpose of model view transformation is to allow a user to re-orient and re-size these objects and place them at any desired location.

Example: Suppose we want to rotate a cube 30 degrees and place it 5 unit length away from the camera for drawing. You might write the program intuitively as below:

```    // first rotate about the x axis by 30 degrees
glRotatef(30, 1, 0, 0);
// then translate back 5
glTranslatef(0, 0, -5);
// use the GLUT library's cube routine to draw
// a  cube centered(by default) at the origin```

The following figure shows that:

If you run this program, you might be surprised to find that nothing appears in the picture! Think about WHY.

If we modify the program slightly as below:

```    // first translate back 5
glTranslatef(0, 0, -5);
// then rotate about the x axis by 30 degrees
glRotatef(30, 1, 0, 0);
// use the GLUT library's cube routine to draw
// a  cube centered(by default) at the origin```

The following figure shows that:

### D. Modeling Transformation v.s. Viewing Transformation

• OpenGL uses concepts of a modeling transformation and a viewing transformation.
• The modeling transformation is the calculations for creating and laying out your model (making sure everything is correctly positioned and oriented relative to everything else in the model). The transformation functions mentioned earlier, glScale*(), glRotate*() and glTranslate*() can be used to alter the modeling matrix.
• The viewing transformation is the calculations for viewing the model (positioning the viewpoint so that you view the model from the orientation and position your desire). You could also use the combination of glScale*(), glRotate*() and glTranslate*() for viewing transformations. The following discussion explains how this approach works. However, it involves the concepts of local and global coordinates and could be very confusing to some students. I would like to suggest students to skip this part first (as I labeled it OPTIONAL), and proceed with the easy approach, gluLookAt(), discussed next.
OPTIONAL
• First let's look at the following code:
• ```    glTranslatef(0, 0, -5);
glRotatef(30, 0, 1, 0);
DrawTheModel();```

Using the local coordinate, we first move the local coordinate's origin down the negative z-axis by 5 units and then rotate that coordinate system about y-axis by 30 degrees.

Using the global coordinate, we first rotate the global coordinate system about its origin by -30 degrees, then move the global coordinate system's origin down the positive z-axis by 5 units. The model is fixed, but the global coordinate system is rotated and translated. The viewpoint locates at the origin of the global coordinate system. Remember that in the global coordinate approach, the order is reversed, and the orientation order is also reversed.

The following is the local approach to a rotation:

The following is the global approach to a rotation:

The gluLookAt() Function: define a viewing transformation

```void gluLookAt( GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble centerx, GLdouble centery, GLdouble centerz,
GLdouble upx, GLdouble upy, GLdouble upz )```
Parameters
```  eyex, eyey, eyez: specifies the position of the eye point;
centerx, centery, centerz: specifies the position of the reference point;
upx, upy, upz: specifies the direction of the up vector.```

Note that, these parameters are equivalent to the camera parameters specified in the textbook:

• (VRPx, VRPy, VRPz) == (eyex, eyey, eyez)
• (AIMx, AIMy, AIMz) == (centerx, centery, centerz)
• (VPNx, VPNy, VPNz) == (centerx – eyex, centery – eyey, centerz – eyez)
• (VUPx, VUPy, VUPz) == (upx, upy, upz)

The gluLookAt() function makes it easy to move both the "from" and the "to" points in a linear manner. For example, if you need to pan along the wall of a building located away from the origin and aligned along no axes in particular, you could simply take the "to" point to be one corner of the building and calculating the "from" as a constant distance from the "to" point. To pan along the building, just vary the "to" point.

• The Components of the Modeling Matrix: All this confusion over modeling and viewing transformation stems from the fact that OpenGL use one matrix to all the transformation matrices, both modeling and viewing. The matrix is called the Modelview matrix. The transformation used to describe and the model used to describe the viewpoint's location and orientation coexist in one matrix, called the Modelview matrix in OpenGL. This matrix results in a simpler set of calculations in the graphics pipeline. Matrix multiplication is not commutative but rather associative, which means that the product of ((AB)C) is the same as (A(BC)). Thus OpenGL's Modelview matrix is logically the product of a viewing matrix and a modeling matrix.

Mmodelview = Mviewing * Mmodeling

What this means is that your viewing transformations must be entered into the Modelview matrix before modeling transformations.

### E. Manipulating the Matrix Directly

• Whichever method you use, you will almost always use the three commands glLoadIdentity(), glPushMatrix() and glPopMatrix(). The first replaces the current matrix with the identity matrix, and the next two commands are used to push and pop the current matrix. Both of glPushMatrix() and glPopMatrix() are applied to temporarily store matrices on a stack. OpenGL has three matrix modes: one for the Modelview, one for the texture, and one for the projection. Generally the stack depth is at least 32 for the Modelview matrix. Pushing a matrix pushes the current matrix stack down and duplicates the current matrix. Popping a matrix replaces the current matrix with the matrix on the top of the stack and removes that matrix from the stack.
• Three OpenGL commands are provided to directly manipulate the current matrix: glLoadIdentity(), glMultMatrix*() and glLoadMatrix*(). The glMultMatrix*() is used to perform matrix multiplication. glMultMatrix*(m) multiplies the current matrix with the one specified in m. That is, if M is the current matrix and T is the matrix passed to glMultMatrix, then M is replaced with MT. glLoadMatrix*(m) replaces the current matrix with the one specified in m. In the array m, the elements are stored in column order, whereas arrays in C or C++ are stored in row order. Do not be confused about these.

### F. Viewport and Projection Transformations

• Once you have learned Modelview transformations, the next step is to understand projection modes and the Projection matrix.
• >OpenGL has three matrices, any one of which may be the current active matrix.
• The function glMatrixMode() takes one of three enumerated parameters to select the Modelview matrix, the Projection matrix, or the Texture matrix.
• Viewport Transformation

For viewport transformations, glViewport() takes four parameters, which used to specify the lower-left corner coordinates and the width and height of the viewport. Since Windows sends a WM_SIZE message before it sends a WM_PAINT message, the WM_SIZE handler is the ideal place to set the viewpoint parameters and the projection transformation parameters. In the MFC class structure the current view class has an Onsize() member that is good for handling the viewport configuration.

Projection Transformation

Projection Transformations: OpenGL provides two methods of converting 3D images into 2D ones.

• The first is orthographic, or parallel projection. You use this style of projection to maintain the scale of objects and their angles without regard to their apparent distance.

• The second is Perspective projection. This is the most popular choice in 3D graphics. The following figure shows perspective projection.

OpenGL's perspective projection is created by a couple of commands. The first is glFrustum().

```  void glFrustum( GLdouble left, GLdouble right, GLdouble bottom,
GLdouble top, GLdouble near, GLdouble far )

Parameters:
left, right:
Specify the coordinates for the left and right vertical
clipping planes;
bottom, top:
Specify the coordinates for the bottom and top horizontal
clipping planes;
near, far:
Specify the distances to the near and far depth
clipping planes.  Both distances must be positive.  ```

glFrustum() describes a perspective matrix that produces a perspective projection. (left, bottom, -near) and (right, top, -near) specify the points on the near clipping plane that are mapped to the lower left and upper right corners of the window, respectively, assuming that the eye is located at (0, 0, 0). -far specifies the location of the far clipping plane. Both near and far must be positive.

The following shows perspective viewing volume and the glFrustum() parameters

Although glFrustum() is powerful, it is not very intuitive, OpenGL also presents a much simpler perspective command, called gluPerspective(). Like glFrustum() it generates a perspective viewing volume but in a much simpler way.

```  void gluPerspective( GLdouble fovy, GLdouble aspect,
GLdouble zNear, GLdouble zFar )

Parameters:
Fovy:

Specifies the field of view angle, in degrees, in the y
direction;

Aspect:

Specifies the aspect ratio that determines the field of view in
the x direction.  The aspect ratio is the ratio of x (width) to
y (height);

ZNear:

Specifies the distance from the viewer to the near clipping
plane (always positive);

ZFar:

Specifies the distance from the viewer to the far clipping
plane (always positive).

```

gluPerspective() specifies a viewing frustum into the world coordinate system. In general, the aspect ratio in gluPerspective should match the aspect ratio of the associated viewport. For example, aspect=2.0 means the viewer's angle of view is twice as wide in x as it is in y. If the viewport is twice as wide as it is tall, it displays the image without distortion.

The following shows perspective viewing volume and the gluPerspective() parameters

### G. An Example

The following program makes use of the glut library. If you are working at home, you might have to put this library on your computer.

The following program can be created in Visual Studio using these instructions:

• Start Visual Studio 2008
• Select File->New->Project

• A "New Project" Dialogue box will appear.
• Expand the Visual C++ Projects Folder and click on the "Win32" folder.
• From the "Templates:" pane select Win32 Console Project
• Choose a "Name:" and a "Location:" and click "OK"
A "Win32 Application Wizard" Dialogue box will appear.
• Click on Application Settings on the left and select Empty project
A new project has now been created. Now, we will add the main program...
An "Add New Item" Dialogue box will appear.
• Select C++ File(.cpp) and call this file main.cpp.
• In the editor window, cut and paste the following code.
• Build and Run it
• You may need to link to glut32.lib. (Project->Properties expand the Linker folder and select Input)

```// main.cpp
//
// This is an example of a program using glut

#include <GL/glut.h>

void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
/*viewing transformation*/
gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);

glBegin(GL_LINES);
glColor3f(1.0f,0.0f,0.0f); //y is red
glVertex3i(0,2,0);
glVertex3i(0,-2,0);
glColor3f(0.0f,1.0f,0.0f); //x is green
glVertex3i(2,0,0);
glVertex3i(-2,0,0);
glColor3f(0.0f,0.0f,1.0f); //z is blue
glVertex3i(0,0,2);
glVertex3i(0,0,-2);
glEnd();

// The following command ensures that the drawing commands are
// actually executed rather than being stored in a buffer
glFlush();
}

void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500,500);
glutInitWindowPosition(100,100);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
```
The above code draws a basic coordinate system with a green x-axis, a red y-axis, and a blue z-axis.
Right now, you are looking directly down the z-axis so you will not see it.

For more on glut*() see the on-line manual.

## Assignment

#### Goals of this assignment:

• Viewing Transformations: through gluLookAt
• Projection Transformations: through glFrustrum, glPerspective, and glOrtho
• Modeling Transformations: rotation, translation, scaling, glPushMatrix, and glPopMatrix

### Part 1

Start with the example code provided in section G above and make the following changes. Write your answers to the questions in steps 1, 2, 4 and 11.

1. Comment out the gluLookAt() call and replace it with the glTranlatef() with parameters (0.0,0.0,-5.0). Is there any change in the display? Why? Why not?
2. Comment out both the gluLookAt() and glTranslatef() lines. What happens? Why?
3. Restore the gluLookAt() call.
4. Comment out the glFrustum() call and replace it with gluPerspective() with parameters (60.0, 1.0, 1.5, 20.0).
1. What happens when you change the field of view angle to 30.0 (from 60.0)? Why?
2. What happens when you modify the aspect ratio (from 1.0)?
5. Restore the original glFrustum() call. (Commenting out the gluPerspective())
6. Use a new color to draw a wirecube whose center is at (0, 0, 0). You can use glutWireCube(1.0). Add this to the "display" function.
7. Move this cube so that it is centered at (1, 0, 0).
8. Draw a second cube, and rotate it 45 degrees around the y-axis.
9. Place this rotated cube directly above the first cube. It will be centered at (1, 1, 0). Be careful of the order of transformations.
10. The perspective view makes the two cubes look a little awkward. Try using orthographic projection instead of the glFrustum call. The function for that is: glOrtho (the arguments are similar to the glFrustum, but you may want to send it larger left, right, bottom, and top values). See the picture for expected results:
11. Rotate everything (using modeling transformations NOT gluLookAt) so that you are looking down at the top of the boxes and seeing the blue z-axis (and no red y-axis). See the picture for expected results:

If you wanted to leave your x and y axes unchanged, but see the top of the boxes, like this:

how would you change your code?
12. Rotate everything so that you can see all three axes along with the two cubes. See the picture for expected results:

You may use different angles of course.
13. BONUS: Get started early on Lab 4. Use glEnable() or glDisable() with GL_DEPTH_BUFFER, GL_LIGHTING, GL_COLOR_MATERIAL, GL_LIGHT0; add GL_DEPTH_BUFFER_BIT to glClear(); and use glutSolidCube(1.0) in the appropriate places to make a solid lighted version of step 12 like this:

/5 marks