Kinesia Online Course
Computer Graphics
Kinesia LLC, 2003

    1. Introduction
    2. Line Drawing
    3. Drawing Objects
    4. More Drawing Tools
    5. Normal Vectors and Polygonal Models of Surfaces
    6. Viewing I -- Affine Transformations
    7. Viewing II -- Projections
    8. Color
    9. Lighting
    10. Blending, antialiasing, fog ..
    11. Display Lists, Bitmaps and Images
    12. Texture Mapping
    13. Curves and Surfaces
    14. Games and SDL

    Viewing II -- Projections

    1. Projection Transformations

    2. Purpose -- define a viewing volume
      1. to determine how an object is projected onto the screen,
      2. to define which objects or portions of objects are clipped out of the final image

    3. Perspective Projection
      • The farther an object is from the "camera", the smaller it appears in the final image.
      • Accomplished through the use of a frustum shaped viewing volume.
      • Objects that fall within the viewing volume are projected toward the apex of the pyramid, where the camera or viewpoint is.
        void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
        Creates a matrix for a perspective-view frustum and multiplies the current matrix by it. The frustum's viewing volume is defined by the parameters: (left, bottom, -near) and (right, top, -near) specify the (x, y, z) coordinates of the lower-left and upper-right corners of the near clipping plane; near and far give the distances from the viewpoint to the near and far clipping planes. They should always be positive.

        Let

          l   =   left     r   =   right
          b   =   bottom     t   =   top
          n   =   near     f   =   far

        The transformation matrix is given by

        ( i.e. corresponds to glFrustum( l, r, b, t, n, f ); )

      • Alternatively, may use gluPerspective() to create the viewing volume:

        1. specify the angle of the field of view ( θ ) in the y-direction
        2. specify the aspect ratio of width to height ( x/y )

        • Calculating Field of view

        tan (θ/2) =     ½ size    
        Distance

        Therefore

            θ =   2 tan-1     ½ size    
        Distance

        #define PI 3.14159265389
        
        double calculateAngle(double size, double distance)
        {
            double radtheta, degtheta; 
        
            radtheta = 2.0 * atan2 (size/2.0, distance);
            degtheta = (180.0 * radtheta) / PI;
            return (degtheta);
        }
          

        Example: Suppose all the coordinates in your object satisfy the equations

        -1 ≤ x ≤ 3, 5 ≤ y ≤ 7, and -5 ≤ z ≤ 5.

        and the 'camera' is at ( 8, 9, 10 )

        Then the center of the bounding box is (1, 6, 0).
        The radius r of a bounding sphere is the distance from the center of the box to any corner, , say (3, 7, 5). That is,

        r = ½ size   =

        Thus,

        Distance d   =

        and

            θ = 2 tan-1 ( r / d ) = 2 tan-1   5.477  
          12.570
        = 2 x 23.54o = 47.08o

        void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble near, GLdouble far);
        Creates a matrix for a symmetric perspective-view frustum and multiplies the current matrix by it. fovy is the angle of the field of view in the x-z plane; its value must be in the range [0.0,180.0]. aspect is the aspect ratio of the frustum, its width divided by its height. near and far values are the distances between the viewpoint and the clipping planes, along the negative z-axis. They should always be positive.

    4. Orthographic Projection

      • the viewing volume is a rectangular parallelepiped ( i.e. a box )
      • the size of the viewing volume does not change from one end to the other
      • used for applications for architectural blueprints and CAD ( computer aided design )

        void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
        Creates a matrix for an orthographic parallel viewing volume and multiplies the current matrix by it. (left, bottom, -near) and (right, top, -near ) are points on the near clipping plane that are mapped to the lower-left and upper-right corners of the viewport window, respectively. (left, bottom, -far) and (right, top, -far) are points on the far clipping plane that are mapped to the same respective corners of the viewport. Both near and far can be positive or negative.

        The corresponding transformation matrix for glOrtho(l, r, b, t, n, f ) is:

    5. Viewing Volume Clipping
        After the projection matrices, any primitives that lie outside the viewing volume are clipped.
    6. Viewport Transformation

    7. Manipulating the Matrix Stacks

      void glPushMatrix(void);
      void glPopMatrix(void);

    8. Additional Clipping Planes

    9. In addition to the six clipping planes of the viewing volume (left, right, bottom, top, near, and far), you can define up to six additional clipping planes to further restrict the viewing volume.

    10. Each plane is specified by the coefficients of its equation:
        Ax + By + Cz + D = 0.
      void glClipPlane(GLenum plane, const GLdouble *equation);
      Defines a clipping plane. The equation argument points to the four coefficients of the plane equation, Ax + By + Cz + D = 0.
      plane is GL_CLIP_PLANEi, where i is an integer specifying which of the available clipping planes to define.

      You need to enable each additional clipping plane you define:

        glEnable(GL_CLIP_PLANEi);

      You can disable a plane with

        glDisable(GL_CLIP_PLANEi);

      Example: Clipped Wireframe Sphere
      /*
       *  clip.cpp
       *  This program demonstrates arbitrary clipping planes.
       */
      #include <GL/glut.h>
      
      void init(void)
      {
         glClearColor (0.0, 0.0, 0.0, 0.0);
         glShadeModel (GL_FLAT);
      }
      
      void display(void)
      {
         GLdouble eqn[4] = {0.0, 1.0, 0.0, 0.0};
         GLdouble eqn2[4] = {1.0, 0.0, 0.0, 0.0};
      
         glClear(GL_COLOR_BUFFER_BIT);
      
         glColor3f (1.0, 1.0, 1.0);
         glPushMatrix();
         glTranslatef (0.0, 0.0, -5.0);
      /*    clip lower half -- y < 0          */
         glClipPlane (GL_CLIP_PLANE0, eqn);
         glEnable (GL_CLIP_PLANE0);
      /*    clip left half -- x < 0           */
         glClipPlane (GL_CLIP_PLANE1, eqn2);
         glEnable (GL_CLIP_PLANE1);
      
         glRotatef (90.0, 1.0, 0.0, 0.0);
         glutWireSphere(1.0, 20, 16);
         glPopMatrix();
      
         glFlush ();
      }
      
      void reshape (int w, int h)
      {
         glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
         glMatrixMode (GL_PROJECTION);
         glLoadIdentity ();
         gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 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;
      }
          

    11. Examples of Composing Several Transformations

      Note again: M2M1 ≠ M1M2

      Note that the equivalent order of operation on P is : scale, rotate, translate

      Suppose we want to rotate an object about a 'z' axis through ( -1, 0 ). We can achieve this by moving everything by 1, do the rotation, and move things back by 1.

      Question: Which of the following is correct?

      Example: Building an Articulated Robot Arm

    12. Scale a cube as a segment of the robot arm
    13. Call the appropriate modeling transformations to orient each segment
    14. As a rotating axis always passes through origin ( 0, 0, 0 ), if we want to rotate an object about another axis (e.g. an axis passing through a cube edge), we first move the local coordinate system ( object ) to one edge of the cube ( pivot point for rotating ) using glTranslatef(); after rotation, we move the system ( object ) back with glTranslatef():

      glTranslatef (-1.0, 0.0, 0.0);
      glRotatef ((GLfloat) shoulder, 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();
      

    15. The second segment is built by moving the local system to the next pivot point:

      glTranslatef (1.0, 0.0, 0.0);	 
      glRotatef ((GLfloat) 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();
          

    16. The complete program:

      
      //robot.cpp
      #include <GL/glut.h>
      #include <stdlib.h>
      
      static int shoulder = 0, elbow = 0;
      
      void init(void) 
      {
         glClearColor (0.0, 0.0, 0.0, 0.0);
         glShadeModel (GL_FLAT);
      }
      
      void display(void)
      {
         glClear (GL_COLOR_BUFFER_BIT);
         glPushMatrix();
         glTranslatef (-1.0, 0.0, 0.0);
         glRotatef ((GLfloat) shoulder, 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();
         glTranslatef (1.0, 0.0, 0.0);
         glRotatef ((GLfloat) 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();
      
         glPopMatrix();
         glutSwapBuffers();
      }
      
      void reshape (int w, int h)
      {
         glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
         glMatrixMode (GL_PROJECTION);
         glLoadIdentity ();
         gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
         glTranslatef (0.0, 0.0, -5.0);
      }
      void keyboard (unsigned char key, int x, int y)
      {
         switch (key) {
            case 's':
               shoulder = (shoulder + 5) % 360;
               glutPostRedisplay();
               break;
            case 'S':
               shoulder = (shoulder - 5) % 360;
               glutPostRedisplay();
               break;
            case 'e':
               elbow = (elbow + 5) % 360;
               glutPostRedisplay();
               break;
            case 'E':
               elbow = (elbow - 5) % 360;
               glutPostRedisplay();
               break;
            case 27:
               exit(0);
               break;
            default:
               break;
         }
      }
      
      int main(int argc, char** argv)
      {
         glutInit(&argc, argv);
         glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
         glutInitWindowSize (500, 500); 
         glutInitWindowPosition (100, 100);
         glutCreateWindow (argv[0]);
         init ();
         glutDisplayFunc(display); 
         glutReshapeFunc(reshape);
         glutKeyboardFunc(keyboard);
         glutMainLoop();
         return 0;
      }
         

      Robot Arm with fingers: