  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 = i j k Ax Ay Az Bx By Bz 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:

For 3-D surface:   F(x, y, z) = 0

N = ∂F ∂ x ∂ F ∂ y ∂ F ∂ z 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 = { {-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 = { {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, tindices = 4, tindices = 1 tindices = 0, tindices = 9, tindices = 4 ... each vertex has 3 coordinates ( that's why ..3fv ) vdata[[tindices]] = vdata = -a vdata[[tindices]] = vdata = 0.0 vdata[[tindices]] = vdata = b */ glBegin(GL_TRIANGLES); for (i = 0; i < 20; i++) { /* color information here */ //...... //vertex information below glVertex3fv(&vdata[tindices[i]]); glVertex3fv(&vdata[tindices[i]]); glVertex3fv(&vdata[tindices[i]]); } 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) { GLfloat d = sqrt(v*v+v*v+v*v); if (d == 0.0) { printf("\nErrot: zero length vector"); return; } v /= d; v /= d; v /= d; } //v1[], v2[] are two vectors //out[] holds the crossproduct v1 x v2 void normcrossprod(float v1, float v2, float out) { GLint i, j; GLfloat length; out = v1*v2 - v1*v2; out = v1*v2 - v1*v2; out = v1*v2 - v1*v2; 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, d2, norm; for (j = 0; j < 3; j++) { d1[j] = vdata[tindices[i]][j] - vdata[tindices[i]][j]; d2[j] = vdata[tindices[i]][j] - vdata[tindices[i]][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]]); glVertex3fv(&vdata[tindices[i]]); glNormal3fv(&vdata[tindices[i]]); glVertex3fv(&vdata[tindices[i]]); glNormal3fv(&vdata[tindices[i]]); glVertex3fv(&vdata[tindices[i]]); } 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

Figure: 20, 80, 320 triangles to approximate sphere ```/* Use subdivision to create 80-sided spherical approximation */ void drawtriangle(float *v1, float *v2, float *v3) { glBegin(GL_TRIANGLES); glNormal3fv(v1); glVertex3fv(v1); glNormal3fv(v2); glVertex3fv(v2); glNormal3fv(v3); glVertex3fv(v3); glEnd(); } void subdivide(float *v1, float *v2, float *v3) { GLfloat v12, v23, v31; GLint i; for (i = 0; i < 3; i++) { v12[i] = ( v1[i]+v2[i] ) / 2.0; v23[i] = ( v2[i]+v3[i] ) / 2.0; v31[i] = ( v3[i]+v1[i] ) / 2.0; } normalize(v12); //The normalize procedure 'push out' the vertex ( make it 3-D ) normalize(v23); normalize(v31); drawtriangle(v1, v12, v31); drawtriangle(v2, v23, v12); drawtriangle(v3, v31, v23); drawtriangle(v12, v23, v31); } for (i = 0; i < 20; i++) { subdivide(&vdata[tindices[i]], &vdata[tindices[i]], &vdata[tindices[i]]); } ```

Recursive Subdivision

 ```void subdivide(float *v1, float *v2, float *v3, long depth) { GLfloat v12, v23, v31; GLint i; if (depth == 0) { drawtriangle(v1, v2, v3); return; } for (i = 0; i < 3; i++) { v12[i] = ( v1[i]+v2[i] ) / 2.0; v23[i] = ( v2[i]+v3[i] ) / 2.0; v31[i] = ( v3[i]+v1[i] ) / 2.0; } normalize(v12); normalize(v23); normalize(v31); subdivide(v1, v12, v31, depth-1); subdivide(v2, v23, v12, depth-1); subdivide(v3, v31, v23, depth-1); subdivide(v12, v23, v31, depth-1); } ```

Generalized Subdivision

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

 ```void surf(GLfloat u, GLfloat vertex, GLfloat normal); float curv(GLfloat u); ```

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, float u2, float u3, float cutoff, long depth) { GLfloat v1, v2, v3, n1, n2, n3; GLfloat u12, u23, u32; 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, B,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