CS315 Lab 3: 3D Transformations


Highlights of this lab:

This lab is an introduction to Matrix Transformation

Assignment

After the lab lecture, you have one week to:

Online OpenGL Manual


Lecture Notes

A. A Simplified View of the OpenGL Pipeline

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

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

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);
glLoadIdentity();

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

 

B. Elementary Transformations

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) denotes 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); 

C. The Order of Transformations

D. Modeling Transformation vs. Viewing Transformation

OPTIONAL

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.

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.

E. Manipulating the Matrix Directly

F. Viewport and Projection Transformations

Viewport Transformation

For viewport transformations, glViewport() takes four parameters, which are used to specify the lower-left corner coordinates and the width and height of the viewport. It is best to call it only once you know how big the window is. That means it should be in your resize function.

Projection Transformation

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

Projection is handled by the GL_PROJECTION matrix. Before you modify what projection you are using you must switch to projection matrix mode. When you are done it is advisable to switch back to GL_MODELVIEW:

    glMatrixMode(GL_PROJECTION);
    
    //Set up projectyion
    
    glMatrixMode(GL_MODELVIEW);

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

gluPerspective()

Although glFrustum() is powerful, it is not very intuitive. GLU provides 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

 


Assignment

Goals of this assignment:

Part 1

Start with this code: boxes.cpp. As written, this program draws a basic coordinate system with a green x-axis, a red y-axis, and a blue z-axis.
With the initial camera settings you are looking directly down the z-axis so you will not see it.

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 an equivalent gluPerspective() call. You may have to do some trigonometry to figure out the field of view. You should already know how to calculate the aspect ratio.
    1. What is the original field of view angle? What happens when you change it to 30.0? Why?
    2. The original aspect ratio is 1.0. What happens when you modify the aspect ratio to values higher or lower than 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 after the first, 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 glFrustum, but you may want to send it larger left, right, bottom, and top values). See the picture for expected results:

    Please leave commented gluPerspective and glFrustum calls in your program.
  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 still 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.

/5 marks

Part 2

Click here for some code that draws a robot arm.

  1. First build the program and see how it works. Try pressing lower and uppercase 'e' to move the elbow. Try pressing lower and uppercase 's' to move the shoulder
  2. Now, add three fingers and a thumb to the robot.
    Use glPushMatrix(); and glPopMatrix(); calls to separate the transformations for each digit. Do not attempt to "untransform" with an inverse rotate, translate or scale.
  3. Finally, add some code that will make the finger and thumb move apart when 'f' is pressed and and together when 'F' is pressed. The center of rotation should be at the wrist.
    Your completed robot hand might look something like the following.

  4. BONUS: Add additional and separate controls for each finger. You may provide controls for knuckles if you wish.
  5. BONUS: Use solid shapes, an appropriate projection matrix, basic lighting and the depth buffer to make your robot hand easier to look at.

/5 marks

Deliverables


On-Line References