Exercise 3: OpenGL Viewing

This exercise introduces the transformations used by OpenGl to render a 3D scene
onto the computer screen. The transformation are:

  1. Viewing Transformation - location of the virtual camera in the scene
  2. Modelling Transformation - arrange 3D models in the scene
  3. Projective Transformation - set the camera field-of-view for projection to 2D
  4. Viewport Transformation - tranform the 2D projected scene to screen coordinates

OpenGL Viewing and Modelling transformations are rotation, translation and scaling transforms combined in the 'modelview' matrix. The viewing  transformation must be specified before the modelling transform.

The  projective transform is represented in the 'projection' matrix. OpenGL support both orthographic projection (all lines are projected parallel to each other) and perspective projection (pin-hole camera). Projective and Viewport transforms can be specified in any order.

The following exercises illustrate the use of these transformations to setup and view a 3D scene.


Copy the directory /vol/www/ee/Teaching/Courses/CGI/exercise3



1. Cube View

The program view_cube.c draws a 3D cube under orthographic projection. Compile and run the program.
    make -f make_view_cube
    ./view_cube

(i) Draw a set of unit axis at the origin using:
    glBegin(GL_LINES);
        glColor3f(1,0,0);
        glVertex3f(0,0,0);
        glVertex3f(0,1,0);
        ......
    glEnd();

    Compile and run to view the axis (at the centre of the cube).

(ii) Change the view tranformation specified by function:
        gluLookAt(location, direction, up);
              location - camera position
              direction - camera view direction
               up - vertical direction for view

    The default view is with the camera located at the origin looking along the negative
    z-axis with the y-axis as up.

(iii) The function gluLookAt() is a utility routine which transforms the camera location
      by modifying the 'modelview' matrix. This utility implements the transformation
      using glRotatef() and glTranslatef() functions. Replace the following
      gluLookAt() with the equivalent rotation and translation:

       gluLookAt(0,0,5, 0,0,0, 0,1,0);

       gluLookAt(5,0,5, 0,0,0, 0,1,0);
 

(iv) The modelling transformation is applied to position objects in the scene,
      The modelview matrix is post multiplied by the modelling transformations as they are
      applied to concatenate them into a single matrix.
      Using functions glTranslatef(), glRotatef() and glScalef() translate the
      cube model to be rotated by 45 degrees about the y-axis, tranlated to location (0,0,-5)
      and scaled such that length in the x-direction is twice the y and z directions.

      Note the order in which transformations are speficied is important. They should be specied
      in reverse order to the order:

           glTranslatef();
           glRotatef();
           glScalef();
           draw_cube();

      First the cube is scaled, then rotated about the origin, then translated.

      Adjust the viewing transform to verify that the cube has been moved to the correct position
      and theat the axis remain at the global origin.

(v) An orthographic projection is specified using glOrtho() to project the 3D scene to 2D, all
     objects appear the same size irrespective of their discance from the camera. OpenGL support
     perspective projection to create more realistic images using a pin-hole camera model where objects
     further from the camera appear smaller.

    Replace the orthographic projection with a perspective projection specified using
    the function glFrustum(which defines the view frustum for perspective projection.
    The view frustum or viewing volume (part of the scene which is visible) is a pyramid with its appex
    at the camera viewpoint. The visible section of the pyramid is the volume between the 'near' and
    'far' planes which are orthogonal to the view direction.

    glFrustum(left,right,bottom,top,near,far);
        (left,bottom,-near) and (right,top,-near) are the lower-left and upper-right coordinates of the
        front plane of the view frustum,. near and far are the distances from the camera centre to the
        front and back plane.

    Try viewing along the z-axis projection with the following parameters:
    glFrustum(-1.0,1.0,-1.0,1.0,1.5, 20.0);

    Change the distance to the near and far planes - what is the effect?

(vi) You can also setup the perspective projection  with the utility function gluPerspective()
      as follows:

    gluPerspective(fovy,aspect,near,far);
            fovy - angle of field of view
            aspect - ratio of frustum width/height
            near/far - distance to near and far cutoff plances

    Replace the perspective projection defined in (v) with gluFrustum() to use the equivalent
    gluPerspective()

    Note: gluPerspective() is limited to creating frustums which are symmetric about the line of sight.

(vii) Add a GLUT keyboard callback function keyboard() to adjust the viewing, modelling and projection transformations in responce to specific keys:

    static float distance=5.0;

    void keyboard(unsigned char key, int x, int y)
    {
            switch (key) {
                case 'd':
                        distance = distance +5;
                        glutPostRedisplay();
                        break;
               case 'D':
                        distance = distance -5;
                        glutPostRedisplay();
                        break;
                default:
                        break;
            }
    }

    and in the main function register the glut callback function:
    glutKeyboardFunc(keyboard);

    Change the viewing transform to:

        glLookAt(0,0,distance, 0,0,0, 0,1,0);

    The cube should zoom in and out using the keys 'd' and '-D' by moving the camera
    center along the z-axis.

    Now try adding keyboard commands to change:
        - model rotation/translation
        - perspective near/far plane distance.



2. Planets

This exercise introduces the use of model transformations to manipulate multiple objects

(i) Compile and run the program planets.c
    make -f make_planets
    ./planets

    This program shows a single sphere which can be rotated about its axis using
    the 'y' key (year).

    Note glPushMatrix()/glPopMatrix() are used to save and restore the modelview matrix
    after transforming the sphere.

(ii) Now add a second sphere which rotates around the first axis according to the year
     and also about its own axis  according to a parameter 'day':

    glTranslatef(2.0,0.0,0.0);
    glRotatef(day,0.0,1.0,0.0);
    glutWireSphere(0.2,10,8);

    Note the order the transformations translate/rotate is such  the new sphere is first rotated
    about the origin according to angle day. Placing the model transforation after the transformation
    of the first sphere will then rotate it about the origin again.

    Add key bindings for 'd' and 'D' to increase and decrease the parameter day.

(iii) Add an third sphere rotating about the new sphere according to the parameter 'time'.
      Use the matrix stack glPushMatrix()/glPopMatrix() to save and restore the modelview
      matrix.

(iv) Tilt the axis of the original sphere.
 



3. Articulation

The matrix stack can also be used to maniputate articulated structures with chains of joints.

(i) The program arm.c shows a single segment of an articulated arm which is rotated about it end as the
    pivot point by the angle 'shoulder'. Compile and run the program and interactively control the
    shoulder rotation with 's' and 'S'.

    Look at the display() function to understand how the arm segment is implemented
    using the matrix stack together with rotation, translation and scaling transforms.
    Applying the transforms in reverse order the following transforms are perfomed on the
    wire cube:
        a) glutWireCube() - generate a cube centered on the origin
        b) glScalef() - scale the cube to a  rectangular segment shape
        c) glTranslatef() - translate along the x-axis cube so that the origin is at one end (the pivot point)
        d) glRotatef() - about the origin by angle 'shouder'
        e) glTranslatef() - translate back to the original origin

    Note how glPushMatrix() and glPopMatrix are used to isolate the various transforms.

(ii) Add a second segment to rotate about a pivot point at end of the first segment using the
      following transformation. Here the final translation is performed to move the pivot point of
      the second segment to cooincide with the first segment.

     Make sure you understand how these transformations are concatenated together.

        glTranslatef(1.0,0.0,0.0);
        glRotatef(elbow,0.0,0.0,1.0);
        glTranslatef(1.0,0.0,0.0);
        glPushMatrix();
        glScalef(2.0,0.4,1.0);
        glutWireCube(1.0);
        glPopMatrix();
 

(iii) Now add a third segment of half the size whose angle relative to the axis of the second segment
      is controlled by a parameter 'wrist'.

(iv) Give the arm two fingers segments at the end of the wrist each with independent angle control.
 



5. Track Ball

If you have time to spare.....

A convenient way to control the user viewpoint is via a virtual track ball controlled by holding
down the mouse buttons. See Angel p185. Implement a track ball for any of the above programs
to allow you to view the 3D models you've created from any direction.