Chapter 4 - CG (2024)
Chapter 4 - CG (2024)
Faculty of Technology
Department of Computer Science
CoSc3072 – Computer Graphics
Chapter 4 Handout –Transformations
Introduction
In this chapter we will introduce some important theoretical foundation for the next chapter on the
viewing pipeline. We will review the mathematics of matrix transformations, and see how matrices can
be used to perform different types of transformation: translations, rotations and scaling. We will
introduce the important topic of homogeneous coordinates – a variation on the standard Cartesian
coordinate system that is widely employed in computer graphics. Finally we will consider how
transformations can be defined in OpenGL.
1. Geometric Transformation
Operations that are applied to the geometric description of an object to change its position, orientation,
or size are called geometric transformations.
Therefore, some graphics packages provide two sets of transformation routines, while other packages
have a single set of functions that can be used for both geometric trans formations and modeling
transformations .
First of all let us review some basics of matrices. 2x2 matrices can be multiplied according to the
following equation.
Matrices of other sizes can be multiplied in a similar way, provided the number of columns of the first
matrix is equal to the number of rows of the second. Matrix multiplication is not commutative. In other
words, for two matrices A and B, AB≠BA. We can see this from the following example.
However, matrix multiplication is associative. This means that if we have three matrices A, B and C, then
(AB) C = A (BC). We can see this from the following example.
In the following sections we will consider how to perform certain common types of coordinate
transformations using matrices. We will start off by looking only at 2-D points, i.e. points that have an x
and a y coordinate. Later in this chapter we will extend our discussion to 3-D points.
The geometric-transformation functions that are available in all graphics packages are those for
translation, rotation, and scaling. Other useful transformation routines that are sometimes included in a
package are reflection and shearing operations.
The translation transformation shifts all points by the same amount. Therefore, in 2-D, we must define
two translation parameters: the x-translation tx and the y-translation ty. A sample translation is
illustrated in Figure 1.
Therefore, from Eq. (5) we can see that the relationship between points before and after the translation is:
px px tx ………………………………………………………………….. (6)
py py ty ………………………………………………………………….. (7)
The rotation transformation rotates all points about a centre of rotation. Normally this centre of
rotation is assumed to be at the origin (0,0), although as we will see later on it is possible to rotate about
any point. The rotation transformation has a single parameter: the angle of rotation, θ. A sample
rotation in 2-D about the origin is illustrated in Figure 2.
Therefore, from Eq. (9) we can see that the relationship between points before and after the rotation is:
The scaling transformation multiplies each coordinate of each point by a scale factor. The scale factor
can be different for each coordinate (e.g. for the x and y coordinates). If all scale factors are equal we
call it uniform scaling, whereas if they are different we call it differential scaling. A sample scaling is
shown in Figure 3.
To scale a point P by scale factors Sx and Sy we apply the scaling matrix S:
When we will look at the viewing pipeline for computer graphics: every primitive undergoes a sequence
of transformations before being displayed. As we must perform a sequence of transformations in this
pipeline, it is essential that we have an efficient way to execute these transformations.
One answer is to compose the series of transformations into a single matrix, and then apply the
composed matrix to every primitive. This would be efficient because we perform the composition once
only (the transformation will be the same for all primitives), leaving only a single matrix multiplication
for each primitive. Since matrix multiplications are often executed in dedicated hardware on the video
card this will be very fast.
There is a problem with this solution, however. We can illustrate this problem by looking at the
following two examples.
Example 1
We want to transform a large number of points by the same sequence of three matrix transformations:
a rotation R1, followed by a scaling S1 and finally another rotation R2. In this case, the overall
transformation is P R2 S1R1P. Therefore we can implement this by composing R2, S1 and R1 into a
single composite matrix C, and then multiplying each point by C.
Example 2
We want to transform a large number of points by the same sequence of four matrix transformations: a
translation T1, a rotation R1, another translation T2 and finally a scaling S1. In this case, the overall
Homogeneous coordinates allow us to do just this. With homogeneous coordinates we add an extra
coordinate, the homogenous parameter, to each point in Cartesian coordinates. So 2-D points are stored
as three values: the x-coordinate, the y-coordinate and the homogeneous parameter. The relationship
between homogeneous points and their corresponding Cartesian points is:
x x / h
Homogeneous point = y , Cartesian point = y / h
h 1
Normally the homogenous parameter is given the value 1, in which case homogenous coordinates are
the same as Cartesian coordinates but with an extra value which is always 1. In the following sections we
will see how adding this extra homogeneous parameter helps us to express translation transformations
using matrix multiplications.
2.1 2-D Translation with Homogenous Coordinates
Now we can express a translation transformation using a single matrix multiplication, as shown below.
Therefore px px tx , py py ty , exactly the same as before, but we used a matrix multiplication
instead of an addition.
Rotations can also be expressed using homogenous coordinates. The following equations are similar to
the form of the 2-D rotation given in Eqs. (8)-(11), with the exception that the rotation matrix R has an
extra row and extra column.
Therefore px px cos py sin and py py cos py sin , which is the same outcome as
before.
Finally, we can also express scaling using homogeneous coordinates, as shown by the following
equations.
3. Matrix Composition
An example of this sequence of transformations is shown in Figure 4. Here we perform a rotation about
the pivot point (2,2) by translating by (-2,-2) to the origin, rotating about the origin and then translating
by (2,2) back to the pivot point. Let us denote our transformations as follows:
T1 is a matrix translation by (-2,-2)
R is a matrix rotation by θo about the origin
T2 is a matrix translation by (2,2)
Therefore, using homogenous coordinates we can compose all three matrices into one composite
transformation, C:
C = T2RT1 ………………………………………………………………….. (24)
Therefore because T1 is right next to the point P it gets applied first, followed by the next transformation
to the left, R, and so on.
The concept of homogenous coordinates is easily extended into 3-D: we just introduce a fourth
coordinate in addition to the x, y and z-coordinates. In this section we review the forms of 3-D
translation, rotation and scaling matrices using homogeneous coordinates.
The 3-D homogeneous coordinate’s translation matrix is similar in form to the 2-D matrix, and is given
by:
1 0 0 t
0 1 0 t y
T ………………………………………….. (25)
0 0 1 t
z
0 0 0 1
We can see that 3-D translations are defined by three translation parameters: tx, ty and tz. We apply this
transformation as follows:
COMPUTER GRAPHICS Page 7
Therefore px px tx , py py ty and pz pz tz .
4.2 3-D Scaling with Homogeneous Coordinates
Similarly, 3-D scaling are defined by three scaling parameters, Sx, Sy and Sz. The matrix is:
Sx 0 0 0
0 Sy 0 0
S
0 0 Sz 0
0 0 0 1
We apply this transformation as follows:
For rotations in 3-D we have three possible axes of rotation: the x, y and z axes. (Actually we can rotate
about any axis, but the matrices are simpler for x, y and z axes.) Therefore the form of the rotation
matrix depends on which type of rotation we want to perform.
For a rotation about the x-axis the matrix is:
5. Other transformation
5.1 Shearing
A transformation that distorts the shape of an object such that the transformed shape appears as if the
object were composed of internal layers that had been caused to slide over each other is called a shear.
Two common shearing transformations are those that shift coordinate x values and those that shift y
values.
An x-direction shear relative to the x axis is produced with the transformation matrix
x’ = x + shx . y and y’ = y
An y-direction shear relative to the y axis is produced with the transformation matrix
1 0 0
shy 1 0
0 0 1
Which transforms the coordinate positions as
5.1 Reflection
A reflection is a transformation that produces a mirror image of an object. The mirror image for a two-
dimensional reflection is generated relative to an axis of reflec on by rota ng the objec1t a out the
reflection axis. We can choose an axis of reflection in the xy plane or perpendicular to the xy plane.
When the reflection axis is a line in the xy plane, the rotation path about this axis is in a plane
perpendicular to the xy plane. For reflection axes that are perpendicular to the xy plane, the rotation
path is in the xy plane. Following are examples of some common reflections.
A reflection about the x axis flips y coordinates while keeping x coordinates the same. The matrix for this
transformation is
A reflection about the y axis flips x coordinates while keeping y coordinates the same. The matrix for this
transformation is
5.Affine transformations
Affine transformations (in two dimensions, three dimensions, or higher dimensions) have the general
properties that parallel lines are transformed in to parallel lines and finite points map to finite points.
Translation, rotation, scaling, reflection, and shear are examples of affine transformations.
We can always express any affine transformation as some composition of these five transformations.
An affine transformation involving only translation, rotation, and reflection preserves angles and
lengths, as well as parallel lines. For each of these three transformations, line lengths and the angle
between any two lines remain the same after the transformation.
When referring to 3-D coordinate systems, we can distinguish between right-handed and left-handed
coordinate systems. This concept is illustrated in Figure 5. For a right-handed coordinate system, if we
extend the thumb and first two fingers of our right-hand so that they are perpendicular to each other,
then the first finger represents the direction of the x-axis, the second finger the y-axis and then thumb
points in the direction of the z-axis.
Before we look at how to define matrix transformations in OpenGL we must introduce the concepts of
premultiplying and postmultiplying. Whenever a matrix is multiplied by another existing matrix we can
either premultiply or postmultiply. Often, when using a general purpose graphics package we need to
specify a sequence of transformations, so we need to know whether the package will compose these
transformations by premultiplying or postmultiplying.
This is very important because which of these two techniques the package uses determines the order in
which we should specify our transformations. For example, suppose we specify a sequence of matrix
transformations A, B and C (in this order). Using premultiplying, the composite transformation will be
CBA, whereas using postmultiplying it will be ABC. We have already seen in Section 1.1 that matrix
multiplication is not commutative, so these two results will be different. We can see from this example
that when postmultiplying we must define our sequence of transformations in the reverse order to that
in which we want them to be applied. The result of postmultiplying the matrices A, B and C is ABC, so C
is applied first, followed by B and then A.
Whenever we apply a transformation in OpenGL it is applied to a current matrix. In fact, as we will see,
in OpenGL we have several current matrices, but for the moments just remember that there is a current
matrix. Almost all transformations in OpenGL is postmultiply by this current matrix. Therefore when
applying a sequence of transformations we must define them in reverse order. We will see an example
of this later. OpenGL always uses a right-handed coordinate system.
glTranslate*(tx,ty,tz): Postmultiply the current matrix by a translation matrix formed from the
translation parameters tx, ty and tz.
glRotate*(θ,vx,vy,vz): Postmultiply the current matrix by a rotation matrix that rotates by
θo about the axis defined by the direction of the vector (vx,vy,vz).
glScale*(Sx,Sy,Sz): Postmultiply the current matrix by a scaling matrix formed from the
scale factors Sx, Sy and Sz.
glLoadMatrix*(elements16): Replaces the current matrix with the 16-element array element16.
The array should be defined in column-major order (i.e. the first four elements represent the
first column; the next four represent the second column, etc.).
glMultMatrix*(elements16): Postmultiplies the current matrix with the 16-element array
element16. The array should be defined in column-major.
glLoadIdentity(elements16): Replaces the current matrix with a 4x4 identity matrix.
Each current matrix in OpenGL has an associated matrix stack. This is a standard FIFO stack that can be
used to ‘remem er’ different transformations. In fact, the current matrix is actually just the top matrix
on the matrix stack. We will see in the next chapter why matrix stacks can be useful, but for the moment
let us introduce the two functions for manipulating the stack:
glPushMatrix: Copy the current matrix to the next position down in the stack, push all other
matrices down one position. The current matrix (i.e. the top matrix on the stack) is left
unchanged.
glPopMatrix: Destroy the current matrix, and move all other matrices on the stack up one
position.
To finish this chapter, let us look at an example of using these OpenGL routines to define a composite
transformation. Consider the following code:
glLoadIdentity();
glTranslated(2.0, 2.0, 0.0);
glRotated(90.0, 0.0, 0.0, 1.0);
glTranslated(-2.0, -2.0, 0.0);
This is actually the same example as we saw above in Figure 4: a rotation about the pivot point (2,2).
Note from this example that we define the transformations in reverse order (because OpenGL always
post multiplies). This example uses 2-D graphics so the rotation is performed about the z-axis.
The transformation process to produce the desired scene for viewing in computer graphics is
analogous to taking a photograph with a camera. The steps with a camera (or a computer)
might be the following:
After these steps are performed, the picture can be snapped, or the scene can be drawn.
The viewing transformations must precede the modeling transformations in your code, but you
can specify the projection and viewport transformations at any point before drawing occurs.
Figure shows the order in which these operations occur on your computer.
The viewing and modeling transformations you specify are combined to form the modelview
matrix, which is applied to the incoming object coordinates to yield eye coordinates. Next, if
you've specified arbitrary clipping planes to remove certain objects from the scene or to
provide cutaway views of objects, these clipping planes are applied.
#include<GL/glut.h>
int z;
void myinit()
{
glClearColor(1.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0,640.0,0.0,480);
}
void drawtri()
{
glBegin(GL_LINES);
{
glVertex2i(100,100);
glVertex2i(300,100);
glVertex2i(300,100);
glVertex2i(200,300);
glVertex2i(200,300);
glVertex2i(100,100);
glEnd();
}
}
void tran()
{
glColor3f(0.0,0.0,1.0);
glLineStipple(1, 0xF00F);
glLoadIdentity();
glTranslatef(100.0,0.0,0.0);
drawtri();
}
void rot()
{
glColor3f(0.0,1.0,1.0);
glLineStipple(1, 0xfff0);
glLoadIdentity();
glTranslatef(200.0,300.0,0.0);
glRotatef(45.0,0.0,0.0,1.0);
glTranslatef(-200.0,-300.0,0.0);
drawtri();
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT); // clear the screen
glMatrixMode(GL_MODELVIEW);
glColor3f(1.0,0.0,0.0);
glEnable(GL_LINE_STIPPLE);
glLineStipple(1, 0xF0F0);
glLoadIdentity();
drawtri();
switch(z)
{
case 1:
tran();
break;
case 2:
rot();
break;
case 3:
scal();
break;
}
glFlush(); // send all output to the display
}