CG Module3 On
CG Module3 On
Module 3
Geometric Objects and Transformations
3.1 FRAMES IN OPENGL
OpenGL is based on a pipeline model, the first part of which is a sequence of operations on vertices. We can
characterize these geometric operations by a sequence of transformations or, equivalently, as a sequence of
changes of frames for the objects defined by a user program. In a typical application, there are six frames
embedded in the pipeline, although normally the programmer will not see more than a few of them directly. In
each of these frames, a vertex has different coordinates. The following is the order that the systems occur in the
pipeline:
1. Object or model coordinates
2. World coordinates
3. Eye (or camera) coordinates
4. Clip coordinates
5. Normalized device coordinates
6. Window (or screen) coordinates
In most applications, we tend to specify or use an object with a convenient size, orientation, and location in its
own frame called the model or object frame. For example, a cube would typically have its faces aligned with axes
of the frame, its center at the origin, and have a side length of 1 or 2 units. The coordinates in the corresponding
function calls are in object or model coordinates.
The application program generally applies a sequence of transformations to each object to size, orient, and
position it within a frame that is appropriate for the particular application. For example, if we were using an
instance of a square for a window in an architectural application, we would scale it to have the correct proportions
and units, which would probably be in feet or meters. The origin of application coordinates might be a location
in the center of the bottom floor of the building. This application frame is called the world frame and the values
are in world coordinates.
Object and world coordinates are the natural frames for the application program. However, the image that is
produced depends on what the camera or viewer sees. Virtually all graphics systems use a frame whose origin is
and whose axes are aligned with the sides of the camera. This frame is called the
camera frame or eye frame. Because there is an affine transformation that corresponds to each change of frame,
there are 4 × 4 matrices that represent the transformation from model coordinates to world coordinates and from
world coordinates to eye coordinates.
Once objects are in eye coordinates, OpenGL must check whether they lie within the view volume. If an object
does not, it is clipped from the scene prior to rasterization. OpenGL can carry out this process most efficiently if
it first carries out a projection transformation that brings all potentially visible objects into a cube centered at the
origin in clip coordinates.
After this transformation, vertices are still represented in homogeneous coordinates. The division by the w
component, called perspective division, yields three-dimensional representations in normalized device
coordinates.
The final transformation takes a position in normalized device coordinates and, taking into account the viewport,
creates a three-dimensional representation in window coordinates. Window coordinates are measured in units
of pixels on the display but retain depth information. If we remove the depth coordinate, we are working with
two-dimensional screen coordinates.
OpenGL starts with two frames: the eye frame and the object
frame. The model-view matrix positions the object frame relative to the eye frame. Thus, the model-view matrix
converts the homogeneous- coordinate representations of points and vectors to their representations in the eye
frame. Because the model-view matrix is part of the state of the system, there is always a current camera frame
and a current object frame. OpenGL provides matrix stacks, so we can store model-view matrices or, equivalently,
frames.
Initially, the model-view matrix is an identity matrix, so the object frame and eye frame are identical. Thus, if we
do not change the model-view matrix, we are working in eye coordinates. As we saw in Chapter 2, the camera is
at the origin of its frame, as shown in Figure 4.26(a). The three basis vectors in eye space correspond to (1) the
up direction of the camera, the y direction; (2) the direction the camera is pointing, the negative z direction; and
(3) a third orthogonal direction, x, placed so that the x, y, z directions form a right-handed coordinate system. We
obtain other frames in which to place objects by performing homogeneous coordinate transformations that define
new frames relative to the camera frame.
If we regard the camera frame as fixed and the modelview matrix as positioning the object frame relative to the
camera frame, then the model-view matrix,
moves a point (x, y, z) in the object frame to the po d) in the camera frame. Thus, by making d a
suitably large positiv in front of the camera by moving the world frame relative
to the camera frame, as shown in Figure 4.26(b). Note that, as far as the user who is working in world
coordinates is concerned, she is positioning objects as before. The model-view matrix takes care of the relative
positioning of the object and eye frames. This strategy is almost always better than attempting to alter the
positions of the objects by changing their vertices to place them in front of the camera.
Vertices will flow through a number of transformations in the pipeline, all of which will use our homogeneous-
coordinate representation. At the end of the pipeline awaits the rasterizer. At this point, we can assume it will do
its job automatically, provided we perform the preliminary steps correctly.
3.2.1 Modeling the Faces
The cube is as simple a three-dimensional object as we might expect to model and display. There are a number
of ways, however, to model it. A CSG system would regard it as a single primitive. At the other extreme, the
hardware processes the cube as an object defined by eight vertices. Our decision to use surface-based models
implies that we regard a cube either as the intersection of six planes or as the six polygons, called facets, that
define its faces. A carefully designed data structure should support both the high-level application view of the
cube and the low-level view needed for the implementation.
We start by assuming that the vertices of the cube are available through an array of vertices; for example, we
could use the following:
We can adopt a more object-oriented formif we first define a three-dimensional point type as follows:
typedef GLfloat point3[3];
The vertices of the cube can be specified as follows:
OpenGL represents all vertices internally in four-dimensional homogeneous coordinates. Function calls using a
three-dimensional type, such as glVertex3fv, have the values placed into a four-dimensional form within the
graphics system.
We can then use the list of points to specify the faces of the cube. For example, one face is
and we can define the other five faces similarly. Note that we have defined three-dimensional polygons with
exactly the same mechanism that we used to define two-dimensional polygons.
If we think of the cube as a polyhedron, we have an object the cube that is composed of six faces. The faces
are each quadrilaterals that meet at vertices; each vertex is shared by three faces. In addition, pairs of vertices
define edges of the quadrilaterals; each edge is shared by two faces. These statements describe the topology of a
six-sided polyhedron. All are true, regardless of the location of the vertices that is, regardless of the geometry of
the object.
The data specifying the location of the vertices contain the geometry and can be stored as a simple list or array,
such as in vertices[8] the vertex list. The toplevel entity is a cube; we regard it as being composed of six faces.
Each face consists of four ordered vertices. Each vertex can be specified indirectly through its index. This data
structure is shown in Figure 4.30.
3.2.4 The Color Cube
We can use the vertex list to define a color cube. We assign the colors of the vertices of the color solid of (black,
white, red, green, blue, cyan, magenta, yellow) to the vertices. We define a function quad to draw quadrilateral
polygons specified by pointers into the vertex list. We assign a color for the face using the index of the first
vertex. Finally, the colorcube specifies the six faces, taking care to make them all outward-facing as follows:
Note that, unlike most OpenGL state information, the information for vertex arrays resides on the client side, not
the server side hence the function name glEnable- ClientState. The arrays are the same as before and can be set
up as globals:
The first three arguments state that the elements are three-dimensional colors and vertices stored as floats and
that the elements are contiguous in the arrays. The fourth argument is a pointer to the array holding the data.
Next, we have to provide the information in our data structure about the relationship between the vertices and the
faces of the cube. We do so by specifying an array that holds the 24 ordered vertex indices for the six faces:
Thus, the first face is determined by the indices (0, 3, 2, 1), the second by (2, 3, 7, 6), and so on.Note that we
have put the indices in an order that preserves outward-facing polygons. The index array can also be specified as
a global.
Now we can render the cube through use of the arrays. When we draw elements of the arrays, all the enabled
arrays (in this example, colors and vertices) are rendered. We have a few options regarding how to draw the
arrays. We can use the following function:
glDrawElements(GLenum type, GLsizei n, GLenum format, void *pointer)
For our cube, within the display callback we could make six calls to glDraw- Elements, one for each face as
follows:
Thus, once we have set up and initialized the arrays, each time that we draw the cube, we have only six function
calls. We can rotate the cube as before; glDrawElements uses the present state when it draws the cube.
We can do even better though. Each face of the cube is a quadrilateral. Thus, if we use GL_QUADS, rather than
GL_POLYGON, we can draw the cube with the single function call
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, cubeIndices);
because GL_QUADS starts a new quadrilateral after each four vertices.
Using homogeneous coordinates, we work with the representations of points and vectors. A linear transformation
then transforms the representation of a given point (or vector) into another representation of that point (or vector)
and can always be written in terms of the two representations, u and v, as a matrix multiplication: v = Cu, where
C is a square matrix. Comparing this expression with the expression.
When we work with homogeneous coordinates, A is a 4 × 4 matrix that leaves unchanged the fourth (w)
component of a representation. The matrix C is of the form
and is the transpose of the matrix M that we derived in Section 4.3.4. The 12 values can be set arbitrarily, and we
say that this transformation has 12 degrees of freedom. However, points and vectors have slightly different
representations in our affine space. Any vector is represented as
3.4.1 Translation
Translation is an operation that displaces points by a fixed distance in a given direction, as shown in Figure 4.34.
To specify a translation, we need only to specify a displacement vector d, because the transformed points are
given by
for all points P on the object. Note that this definition of translation makes no reference to a frame or
representation. Translation has three degrees of freedom because we can specify the three components of the
displacement vector arbitrarily.
3.4.2 Rotation
Rotation is more difficult to specify than translation because we must specify more parameters. We start with the
simple example of rotating a point about the origin in a two-dimensional plane, as shown in Figure 4.35. Having
specified a particular point the origin we are in a particular frame. A two-dimensional point at (x, y) in this frame
is rotated about the origin by an a can obtain the standard equations describing
this rota ) in polar form:
Expanding these terms using the trigonometric identities for the sine and cosine of the sum of two angles, we find
Rotation and translation are known as rigid-body transformations. No combination of rotations and translations
can alter the shape or volume of an object; the ently,
rotation and translation alone cannot give us all possible affine transformations. The transformation shown in
Figure 4.38 are affine, but they are not rigid-body transformations.
3.4.3 Scaling
Scaling is an affine non rigid-body transformation by which we can make an object bigger or smaller. Figure
4.39 illustrates both uniform scaling in all directions and scaling in a single direction. We need nonuniform
scaling to build up the full set of affine transformations that we use in modeling and viewing by combining a
properly chosen sequence of scalings, translations, and rotations.
Scaling transformations have a fixed point, as we can see from Figure 4.40. Hence, to specify a scaling, we can
specify the fixed point, a direction in which we ct gets
longer in the specified give
us reflection (Figure 4.41) about the fixed point, in the scaling direction. Scaling has six degrees of freedom
because we can specify an arbitrary fixed point and three independent scaling factors.
3.5.1 Translation
Translation displaces points to new positions defined by
displacing by a distance d, then
Looking at their homogeneous-coordinate forms
This method of representing translation using the addition of column matrices does not combine well with our
representations of other affine transformations. However, we can also get this result using the matrix
multiplication:
it is not possible to find a 3×3 matrix D such that for the given displacement vector d. For this reason,
the use of homogeneous coordinates is often seen as a clever trick that allows us to convert the addition of column
matrices in three dimensions to matrix matrix multiplication in four dimensions.
We can obtain the inverse of a translation matrix either by applying an inversion algorithm or by noting that if
we displace a point by the vector d, we can return to the
method, we find that
3.5.2 Scaling
For both scaling and rotation, there is a fixed point that is unchanged by the transformation. We let the fixed point
be the origin, and we show how we can concatenate transformations to obtain the transformation for an arbitrary
fixed point.
A scaling matrix with a fixed point of the origin allows for independent scaling along the coordinate axes. The
three equations are
As is true of the translation matrix and, indeed, of all homogeneous coordinate transformations, the final row of
the matrix does not depend on the particular transformation, but rather forces the fourth component of the
transformed point to retain the value 1.
We obtain the inverse of a scaling matrix by applying the reciprocals of the scale factors:
3.5.3 Rotation
We first look at rotation with a fixed point at the origin. There are three degrees of freedom corresponding to our
ability to rotate independently about the three coordinate axes. We have to be careful, however, because matrix
multiplication is not a commutative operation (Appendix C). Rotation about the x-axis by an angle y
rotation about the y- oes not give us the same result as the one that we obtain if we reverse
the order of the rotations.
We can find the matrices for rotation about the individual axes directly from the results of the two-dimensional
rotation. We saw that the two-dimensional rotation was actually a rotation in three dimensions about the z-axis
and that the points remained in planes of constant z. Thus, in three dimensions, the equations for rotation about
the z-
We can derive the matrices for rotation about the x- and y-axes through an identical argument. If we rotate about
the x-axis, then the x values are unchanged, and we have a two-dimensional rotation in which points rotate in
planes of constant x; for rotation about the y-axis, the y values are unchanged. The matrices are
The signs of the sine terms are consistent with our definition of a positive rotation in a right-handed system.
Suppose that we let R denote any of our three rot can always be undone by a
In addition, noting that all the cosine terms are on the diagonal and the sine terms are off-diagonal, we can use
the trigonometric identities
Using the fact that the transpose of a product is the product of the transposes in the reverse order, we see that for
3.5.4 Shear
Although we can construct any affine transformation from a sequence of rotations, translations, and scalings,
there is one more affine transformation the shear transformation that is of such importance that we regard it as
a basic type, rather than deriving it from the others. Consider a cube centered at the origin, aligned with the axes
and viewed from the positive z-axis, as shown in Figure 4.42. If we pull the top to the right and the bottom to the
left, we shear the object in the x direction. Note that neither the y nor the z values are changed by the shear, so
we can call this operation x shear to distinguish it from shears of the cube in other possible directions. Using
simple trigonometry on Figure 4.43, we see that each shear is characterized by
this shear are
We can obtain the inverse by noting that we need to shear in only the opposite direction; hence,
If we are to transform a single point, this order is the most efficient because each matrix multiplication involves
multiplying a column matrix by a square matrix. If we have many points to transform, then we can proceed in
two steps. First, we calculate
M = CBA.
Then, we use this matrix on each point
q = Mp.
This order corresponds to the pipeline shown in Figure 4.45, where we compute M first, then load it into a pipeline
transformation unit. If we simply count operations, we see that although we do a little more work in computing
M initially, because M may be applied to tens of thousands of points, this extra work is insignificant compared
with the savings we obtain by using a single matrix multiplication for each point. We now derive examples of
computing M.
This sequence is shown in Figure 4.47. In terms of our basic affine transformations,
l is T(pf ). Concatenating them together, we obtain the single matrix
The instance transformation is applied in the order shown in Figure 4.52.Objects are usually defined in their own
frames, with the origin at the center of mass and the sides aligned with the model frame axes. First, we scale the
object to the desired size. Then we orient it with a rotation matrix. Finally, we translate it to the desired orientation.
Hence, the instance transformation is of the form M = TRS.
Modeling with the instance transformation works well not only with our pipeline architectures but also with the
display lists. A complex object that is used many times can be loaded into the server once as a display list.
Displaying each instance of it requires only sending the appropriate instance transformation to the server before
executing the display list.
Nonetheless, to find an affine matrix to represent this transformation, we have to assume that we are in some
frame.
The vector about which we wish to rotate the cube can be specified in various ways. One way is to use two points,
p1 and p2, defining the vector u = p2 p1.
Note that the order of the points determines the posit and that even though we draw
u as passing through p0, only the orientation of u matters. Replacing u with a unit-length vector
in the same direction simplifies the subsequent steps. We have already seen that moving the fixed point to the
origin is a helpful technique. Thus, our first transformation
After the initial translation, the required rotation problem is as shown in Figure 4.54.
Our previous example (see Section 4.9.2) showed that we could get an arbitrary rotation from three rotations
about the individual axes. This problem is more difficult because we do not know what angles to use for the
individual rotations. Our strategy is to carry out two rotations to align the axis of rotation, v, with the z-axis. Then
we ca -axis, after which we can undo the two rotations that did the aligning. Our final
rotation matrix will be of the form
GLfloat vertices[][3]={{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0},{1.0,1.0,-1.0},{-1.0,1.0,-1.0},
{-1.0,-1.0,1.0},{1.0,-1.0,1.0},{1.0,1.0,1.0},{-1.0,1.0,1.0}};
GLfloat colors[][3]={{0.0,0.0,0.0},{0.0,0.0,1.0},{0.0,1.0,0.0},{0.0,1.0,1.0},{1.0,0.0,0.0},
{1.0,0.0,1.0},{1.0,1.0,0.0},{1.0,1.0,1.0}};
void colorcube()
{
polygon(0,3,2,1);
polygon(2,3,7,6);
polygon(1,2,6,5);
polygon(0,4,5,1);
polygon(4,5,6,7);
polygon(0,3,7,4);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(theta[0],1.0,0.0,0.0);
glRotatef(theta[1],0.0,1.0,0.0);
glRotatef(theta[2],0.0,0.0,1.0);
colorcube();
glFlush();
glutSwapBuffers();
}
void spincube()
{
theta[axis]+=2.0;
if(theta[axis]>360.0)
theta[axis]-=360.0;
glutPostRedisplay();
}
OpenGL program to rotate a triangle around the origin and fixed arbitrary point.
#include<GL/glut.h>
#include<stdio.h>
int x,y;
int where_to_rotate=0;
float rotate_angle=0;
float translate_x=0,translate_y=0;
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glColor3f(1,1,1);
draw_pixel(0,0);
if (where_to_rotate == 1)
{
translate_x = 0;
translate_y = 0;
rotate_angle += 1;
}
if (where_to_rotate == 2)
{
translate_x = x;
translate_y = y;
rotate_angle += 1;
glColor3f(0,0,1);
draw_pixel(x,y);
}
glTranslatef(translate_x, translate_y, 0);
glRotatef(rotate_angle, 0, 0, 1);
glTranslatef(-translate_x, -translate_y, 0);
triangle(translate_x,translate_y);
glutPostRedisplay();
glutSwapBuffers();
}
void init()
{
glClearColor(0,0,0,1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-800, 800, -800, 800);
glMatrixMode(GL_MODELVIEW);
}
if(option==2)
where_to_rotate=2;
if(option==3)
where_to_rotate=3;
}