Syllabus   Blank Homework   Quizzes  
Notes   Labs   Scores   Blank


Lecture Notes
Dr. Tong Yu, Sept. 2013
All the materials presented here are adopted from the textbook and the listed references.
    1. Introduction
    2. Line Drawing
    3. Drawing Objects
    4. More Drawing Tools
    5. Vertex Array
    6. Normal Vectors and Polygonal Models of Surfaces
    7. Viewing I -- Affine Transformations
    8. Viewing II -- Projections
    9. Color
    10. Lighting
    11. Blending, antialiasing, fog ..
    12. Display Lists, Bitmaps and Images
    Appendix. Games and SDL

    Normal Vectors and Polygonal Models of Surfaces

    1. 3D Vectors

    2. Starndard Unit Vectors

        i = ( 1, 0, 0 ),     j = ( 0, 1, 0 ),     k = ( 0, 0, 1 )

    3. Dot Product and Cross Products

        A = ( Ax, Ay, Az ) = Axi + Ayj + Azk

        Magnitude of A

        |A| = ( Ax2 + Ay2 + Az2 )½

        e.g.
        v = ( 2, 4, -1 ) = 2(1, 0, 0 ) + 4(0, 1, 0) - 1( 0, 0, 1 ) = 2i + 4j - k

        |v| = ( 22 + 42 + 12 )½ = ( 21 )½ = 4.5826

        B = ( Bx, By, Bz )

        Dot Product

        A.B = AxBx + AyBy + AzBz = |A||B| cos

        Cross Product

        A x B = ( AyBz - AzBy )i + ( AzBx - AxBz )j + ( AxBy - AyBx )k

        A x B =
        vertical bar
        i
        j
        k
          Ax     Ay     Az  
          Bx     By     Bz  
        vertical bar

        A x B is always perpendicular to A and B

    4. Linear Combination of Vectors

        w = a1v1 + a2v2 + ... + + amvm

      Affine Combination of Vectors

        a1 + a2 + ... + am = 1

        e.g. ( 1 - t )a + ( t ) b

      Convex Combination of Vectors

        a1 + a2 + ... + am = 1     and     ai ≥ 0     for i = 1, ..., m

    5. Basic Properties

      a, b -- scalars, P, Q, R -- 3D vectors

      • P x Q = - ( Q x P )

      • (a P ) x Q = a( P x Q )

      • P x ( Q + R )= P x Q + P x R

      • P x P = 0 = ( 0, 0, 0 )T

      • ( P x Q ) . R = ( R x P ) . Q = ( Q x R ). P

      • P x ( Q x P ) = P x Q x P = P2 Q - ( P . Q )P

      • a ( P + Q ) = a P + a Q

      • ( P + Q ) + R = P + ( Q + R )
    6. Finding the Normal to a Plane

      A normal vector ( or normal for short ) is a vector that points in a direction that is perpendicular to a surface.
      For a plane ( flat surface ), one perpendicular direction is the same for every point on the surface

      Like a vector, a point P can be specified by any three coordinates (i.e. P = ( Px, Py, Pz ). e.g. P = ( 1, 2, 3 )

      A vector field of normals to a surface

      A normal to a surface at a point is the same as a normal to the tangent plane to that surface at that point.

      Any three points P1, P2, P3 determine a unique plane.
      To find a normal to the plane, we build two vectors

        A = P2 - P1
        B = P3 - P1

      The normal is given by
        n = A x B

      ( Note: Looking at the plane ( i.e. front face ), the points P1 P2, P3 appear counter-clockwise )

      Example:

        Find a normal to the plane that passes through the points (1, 0, 2), (2, 3, 0), and (1, 2, 4)

      Solution:

        By direct substitution, A = ( 2, 3, 0 ) - ( 1, 0, 2) = ( 1, 3, -2 ) and B = (1, 2, 4) - (1, 0, 2) = (0, 2, 2), so their cross product n = ( 10, -2, 2 ).

      Normal and Plane Equation:

      The equation of a plane with normal vector N=(a, b, c)T passing through the point (x0, y0, z0) is given by

        0 = N⋅[(x, y, z) - ( x0, y0, z0 )] =
        a
        b
        c
        x - x0
        y - y0
        z - z0
        = a(x-x0) + b(y-y0) + c(z-z0)
      which is in the form:
        ax + by + cz + d = 0

        where d = -ax0 - by0 - cz0

      Conversely, a normal vector to a plane specified by

        f( x, y, z ) = ax + by + cz + d = 0

      is given by

        N = ( a, b, c ) T

      Class Exercise:

        Find a normal to the plane described by the equation: x + 2y + z - 1 = 0

      General Form:

      Parametric surface P(s,t):

      N = Ts x Tt

      e.g. x = x ( θ, φ )   y = y ( θ, φ )   z = z ( θ, φ )

      A =
      ∂x
      ∂ θ
      ∂ y
      ∂ θ
      ∂ z
      ∂ θ

      B =
      ∂x
      ∂ φ
      ∂ y
      ∂ φ
      ∂ z
      ∂ φ
      N = A x B

      Why normal vectors are important?

      An object's normal vectors define the orientation of its surface in space -- in particular, its orientation relative to light resources. These vectors are used by OpenGL to determine how much light the object receives at its vertices.

      glNormal*() -- set the normal to value the argument passed in
      subsequent calls to glVertex*() cause the specified vertices to be assigned the current normal

      Example:

      	glBegin( GL_POLYGON );
        	  glNormal3fv( n0 );
        	  glVertex3fv( v0 );
        	  glNormal3fv( n1 );
        	  glVertex3fv( v1 );
        	  glNormal3fv( n2 );
        	  glVertex3fv( v2 );
        	  glNormal3f( 1.0, 1.0, 1.0 );
        	  glVertex3f( 2.0, 3.0, 1.0 );
      	glEnd();  
      	

      A vector that has a length of 1 is said to be normalized

      glEnable( GL_NORMALIZE );

      which prevents scaling from changing the normal lengths.

    7. Notes on Building Polygonal Models of Surfaces

      Any surface can be approximated by polygons.

    8. Keep polygon orientations ( windings ) consistent.

    9. When subdividing a surface, we need to watch out for any nontriangular polygons. Three points always lie on the same plane.

    10. Pay attention to the trade-off between the display speed and the quality of the image.

    11. For high-quality images, it's better to subdivide more on the outline edges than in the interior.

    12. Try to avoid T-intersections in the models (see Figure below ).

    13. When constructing a closed surface, make sure we use exactly the same values for coordinates at the beginning and end of a closed loop.
      Example of bad code: ( Why ? )

        	const double PI = 3.1415926;
        	const int EDGES = 30;
        
        	//draw a circle
        	glBegin ( GL_LINE_STRIP );
        	for ( int i = 0; i <= EDGES; i++ )
        	  glVertex2f ( cos ( 2*PI*i/EDGES ), sin ( 2*PI*i / EDGES ) );
        	glEnd ();
        	
    14. Example: Building an Icosahedron ( a solid object with 20 faces of equilateral triangles that span 12 vertices )

      #define a .525731112119133606 
      #define b .850650808352039932
      
      static GLfloat vdata[12][3] = {    
         {-a, 0.0, b}, {a, 0.0, b}, {-a, 0.0, -b}, {a, 0.0, -b},    
         {0.0, b, a}, {0.0, b, -a}, {0.0, -b, a}, {0.0, -b, -a},    
         {b, a, 0.0}, {-b, a, 0.0}, {b, -a, 0.0}, {-b, -a, 0.0} 
      };
      static GLuint tindices[20][3] = { 
         {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1},    
         {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3},    
         {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6}, 
         {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} };
      int i;
      
      /*
      tindices[0][0] = 0, tindices[0][1] = 4, tindices[0][2] = 1
      tindices[1][0] = 0, tindices[1][1] = 9, tindices[1][2] = 4
      ...
      each vertex has 3 coordinates ( that's why ..3fv )
      vdata[[tindices][0][0]][0] = vdata[0][0] = -a
      vdata[[tindices][0][0]][1] = vdata[0][1] = 0.0
      vdata[[tindices][0][0]][2] = vdata[0][2] = b
      
      */
      glBegin(GL_TRIANGLES);    
      for (i = 0; i < 20; i++) {    
         /* color information here */
        //...... 
         //vertex information below
         glVertex3fv(&vdata[tindices[i][0]][0]); 
         glVertex3fv(&vdata[tindices[i][1]][0]); 
         glVertex3fv(&vdata[tindices[i][2]][0]); 
      }
      glEnd();
      
        
      Example: A Twenty-sided die
    15. The strange numbers a and b are chosen so that the distance from the origin to any of the vertices of the icosahedron is 1.0. ( note: a2 + b2 = 1 )
    16. tindices[] tells how to link the vertices to make triangles
    17. Need to calculate normals for surfaces if they are to be lit.

      //normalize a vector
      void normalize(float v[3]) {    
         GLfloat d = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); 
         if (d == 0.0) {
            printf("\nErrot: zero length vector");    
            return;
         }
         v[0] /= d; v[1] /= d; v[2] /= d; 
      }
      
      //v1[], v2[] are two vectors
      //out[] holds the crossproduct v1 x v2 
      void normcrossprod(float v1[3], float v2[3], float out[3]) 
      { 
         GLint i, j; 
         GLfloat length;
      
         out[0] = v1[1]*v2[2] - v1[2]*v2[1]; 
         out[1] = v1[2]*v2[0] - v1[0]*v2[2]; 
         out[2] = v1[0]*v2[1] - v1[1]*v2[0]; 
         normalize(out); 
      }
        

      /*
        Note: difference between two points is a vector
        i.e. 	A = P0 - P1
       	B = P2 -  p1
      	A x B is normal to the plane passing through points P0, P1, P2,
      	and P0, P1, P2 appear counter-clockwise
        ( see notes above )
      */
      GLfloat d1[3], d2[3], norm[3];    
      for (j = 0; j < 3; j++) {    
         d1[j] = vdata[tindices[i][0]][j] - vdata[tindices[i][1]][j];    
         d2[j] = vdata[tindices[i][2]][j] - vdata[tindices[i][1]][j];    
      }
      normcrossprod(d1, d2, norm); 
      glNormal3fv(norm);
        

      For a sphere, a normal vector at a point is the vector from origin to that point.

      glBegin(GL_TRIANGLES); 
      for (i = 0; i < 20; i++) {    
            glNormal3fv(&vdata[tindices[i][0]][0]); 
            glVertex3fv(&vdata[tindices[i][0]][0]); 
            glNormal3fv(&vdata[tindices[i][1]][0]); 
            glVertex3fv(&vdata[tindices[i][1]][0]); 
            glNormal3fv(&vdata[tindices[i][2]][0]); 
            glVertex3fv(&vdata[tindices[i][2]][0]); 
      }
      glEnd();
        
      /* May use:
          ....
          glVertexPointer ( 3, GL_FLOAT, 0, vdata );
          glNormalPointer ( 3, GL_FLOAT, vdata );
          for (int i = 0; i < 20; i++) {
            glColor3f( i/20.0, i/40.0, i/20.0 );
            glDrawElements (GL_TRIANGLES, 3, GL_UNSIGNED_INT, tindices[i] );
          }
          ...
      */
      

      Improving the Model by Subdivision

      Recursive Subdivision

      Generalized Subdivision

      consider an arbitrary surface parameterized by two variables u[0] and u[1]. Suppose that two routines are provided:

      void surf(GLfloat u[2], GLfloat vertex[3], GLfloat normal[3]); 
      float curv(GLfloat u[2]);
        

      If surf() is passed u[], the corresponding three-dimensional vertex and normal vectors (of length 1) are returned. If u[] is passed to curv(), the curvature of the surface at that point is calculated and returned.

      The following shows the recursive routine that subdivides a triangle either until the maximum depth is reached or until the maximum curvature at the three vertices is less than some cutoff.

      
       void subdivide(float u1[2], float u2[2], float u3[2],
                      float cutoff, long depth)
      {
         GLfloat v1[3], v2[3], v3[3], n1[3], n2[3], n3[3];
         GLfloat u12[2], u23[2], u32[2];
         GLint i;
      
         if (depth == maxdepth || (curv(u1) < cutoff &&
      	curv(u2) < cutoff && curv(u3) < cutoff)) {
            surf(u1, v1, n1); surf(u2, v2, n2); surf(u3, v3, n3);
            glBegin(GL_POLYGON);
               glNormal3fv(n1); glVertex3fv(v1);
               glNormal3fv(n2); glVertex3fv(v2);
               glNormal3fv(n3); glVertex3fv(v3);
            glEnd();
            return;
         }
         for (i = 0; i < 2; i++) {
            u12[i] = (u1[i] + u2[i])/2.0;
            u23[i] = (u2[i] + u3[i])/2.0;
            u31[i] = (u3[i] + u1[i])/2.0;
         }
         subdivide(u1, u12, u31, cutoff, depth+1);
         subdivide(u2, u23, u12, cutoff, depth+1);
         subdivide(u3, u31, u23, cutoff, depth+1);
         subdivide(u12, u23, u31, cutoff, depth+1);
      }
        

    18. Solid Modeling with Polygonal Meshes

    19. Polygon mesh -- collection of polygons
    20. Vertex vs. Face Normals
      • Smooth surfaces -- use vertex normal ( e.g. cylinder, sphere )
      • Flat surfaces -- use face normals
    21. Defining a Polygon Mesh
      • Use three separate lists, vertex list, normal list, face list
      e.g. a barn
        Normal nx ny nz
        0 -1 0 0
        1 -0.7071 0.7071 0
        2 0.7071 0.7071 0
        3 1 0 0
        4 0 -1 0
        5 0 0 1
        6 0 0 -1
         
        Face Vertices Associated
        Normal
        0 (left) 0,5,9,4 0,0,0,0
        1 (roof left) 3, 4, 9, 8 1, 1, 1, 1
        2 (roof right) 2, 3, 8, 7 2, 2, 2, 2
        3 (right) 1, 2, 7, 6 3, 3, 3, 3
        4 (bottom) 0, 1, 6, 5 4, 4, 4, 4
        5 (front) 5, 6, 7, 8, 9 5, 5, 5, 5, 5
        6 (back) 0, 4, 3, 2, 1 6, 6, 6, 6, 6

      Implementation

        class Point3{
        public:
                float x,y,z;
        
                void set(float dx, float dy, float dz);
                void set(Point3& p);
                Point3(float xx, float yy, float zz);
                Point3();
        
        };
        
        class VertexID {
        public: 
          int vertIndex;	//index of this vertex in the vertex list
          int normIndex;	//index of this vertex's normal
        };
        //used to define a Mesh
        class Face{
        public:
          int nVerts;
          VertexID * vert; 	//array of vertex and normal indices
        
          Face();	   	//constructor
          ~Face();		//destructor
        };
        
        class Mesh {
        private:
          int numVerts, numNorms, numFaces;
          Point3 *pt; 		// array of points ( 3D vertices
          Vector3 *norm; 	// array of normals
          Face *face; 		// array of faces
          int lastVertUsed;
          int lastNormUsed;
          int lastFaceUsed;
        
        public:
          string meshFileName; 	// holds file name for this Mesh
        
          void readMesh(string fname);
          void writeMesh(char* fname);
          void printMesh();
          void drawMesh();
          void drawEdges();
          void freeMesh();
          int isEmpty();
          void makeEmpty();
          Mesh();
          virtual void drawOpenGL();
          Mesh(string fname);
          Vector3 newell4(int indx[]);
          bool hit(Ray &r, Intersection &inter);
        }; // end of Mesh class
        
        
        void Mesh::drawMesh()   // use OpenGL to draw this mesh
        {
          // draw each face of this mesh using OpenGL: draw each polygon.
          if( isEmpty() ) return; // mesh is empty
        
          for(int f = 0; f < numFaces; f++) // draw each face
          {
            glBegin(GL_POLYGON);
            cout << endl;
            setColor( f );
            for(int v = 0; v < face[f].nVerts; v++) // for each vertex
            {
                int in = face[f].vert[v].normIndex ; // index of this normal
                int iv =  face[f].vert[v].vertIndex ; // index of this vertex
                glNormal3f(norm[in].x, norm[in].y, norm[in].z);
                glVertex3f(pt[iv].x, pt[iv].y, pt[iv].z);
            }
            glEnd();
            cout << endl;
          }
        } //drawMesh
        
    22. Tweening for Art and Animation

      Polyline Q formed by points: Q0, Q1, .., Qi, ... Qn-1

      Polyline R formed by points: R0, R1, .., Ri, ... Rn-1

      We construct polyline P with points: P0, P1, .., Pi, ... Pn-1 by 'interpolating' Q and R, i.e.

        Pi(t) = ( 1 - t ) Qi + tRi

        When   t = 0, Pi = Qi ( i.e. P = Q )

        When   t = 1, Pi = Ri ( i.e. P = R )

      So when t changes from 0, to 0.1, 0.2, ..., to 1, P begins with the shape Q and gradaully morphs to R.
      Point2 tween( Point2 P1, Point2 P2, float t )
      {
        Point2 P;
        P.x = P1.x + ( P2.x - P1.x ) * t;
        P.y = P1.y + ( P2.y - P1.y ) * t;
        return P;
      }
      
      void drawTween(Point2 A[], Point2 B[], int n, float t, Point2 c)
      {   // draw the tween at time t between polylines A and B
        Point2 P0 = tween(A[0], B[0],t);
        cvs.moveTo(P0.x+c.x, P0.y+c.y);
      
        for(int i = 1; i < n; i++)
        {
          Point2 P;
          P = tween(A[i], B[i],t);
          cvs.lineTo(P.x+c.x, P.y+c.y);
        }
        cvs.lineTo(P0.x+c.x, P0.y+c.y);
      }
              
      man ←→ woman