Principles of Computer Graphics Theory and Practice Using OpenGL and Maya
Principles of Computer Graphics Theory and Practice Using OpenGL and Maya
Principles of Computer Graphics Theory and Practice Using OpenGL and Maya
Shalini Govil-Pai
Sunnyvale, CA, U.S.A.
a- Springer
Shalini Govil-Pai
896 Savory Drive,
Sunnyvale, CA 94087
Govil-Pai, Shalini
Principles of Computer Graphics: Theory and Practice Using OpenGL and Maya@/ Shalini
Govil-Pai
p.cm.
Includes bibliographical references and index.
Alias and Maya are registered trademarks of Alias Systems Corp. in the United States
andlor other countries.
springeronline.com
Contents
Preface vii
Section 1 1
1 From Pixels to Shapes 3
1.1 Complex Display Systems 4
1.2 Game Buffers 6
1.3 Coordinate Systems: How to identify pixel points 9
1.4 Shapes and Scan Converting 11
Section 2 81
5 3D Modeling 83
5.1 The 3D System 84
5.2 3D Modeling 90
5.3 3D Modeling Primitive Shapes 95
5.4 3D Modeling Generic Shapes 100
5.5 3D Transformation 104
5.6 Viewing in 3D 107
5.7 Hierarchical Modeling Using Transformations 118
vi
XV~ CONTENTS
7 Advanced Techniques
7.1 Advanced Modeling
7.2 Advanced Rendering Techniques
Section 3
9 Animation
9.1 Traditional Animations
9.2 3D Computer Animation - Interpolations
9.3 The Principles of Animation
9.4 Advanced Animation Techniques
10 Viewpoint Animation
10.1 Animating the Camera inthe Snowy Animation
10.2 Building up a Real Time 3D Game
Appendix A
Appendix B
Appendix C
Bibliography
Index of Terms
Preface
Computer Graphics: the term has become so widespread now, that we rarely stop
to think about what it means. What is Computer Graphics? Simply defined,
Computer Graphics (or CG) is the images generated or modified on a computer.
These images may be visualizations of real data or imaginary depictions of a
fantasy world.
The use of Computer Graphics effects in movies such as The Incredibles and
games such as Myst have dazzled millions of viewers worldwide. The success of
such endeavors is prompting more and more people to use the medium of
Computer Graphics to entertain, to educate, and to explore.
For doctors, CG provides a noninvasive way to probe the human body and to
research and discover new medications. For teachers, CG is an excellent tool to
visually depict concepts to their students. For business people, CG has come to
signify images of charts and graphs used for analysis of data. But for most of us,
CG translates into exciting video games, special effects and entire films-what are
often referred to as CG productions. This entertainment aspect of CG is what has
made it such a glamorous and sought-after field.
Ten years ago, CG was limited to high-end workstations, available only to an
elite few. Now, with the advances in PC processing power and the availability of
3D graphics cards, even high school students can work on their home PC to
create professional quality productions
The goal of this book is to expose you to the fundamental principles behind
modern computer graphics. We present these principles in a fun and simple
manner. We firmly believe that you don't have to be a math whiz or a high tech
computer programmer to understand CG. A basic knowledge of trigonometry,
algebra, and computer programming is more than sufficient.
As you read this book, you will learn the bits and bytes of how to transform
your ideas into stunning visual imagery. We will walk you through the processes
that professionals employ to create their productions, Based on the principles
that we discuss, you will follow these processes step and step, to design and
create your own games and animated movies.
We will introduce you to the OpenGL API—a graphics library that has
become the de facto standard on all desktops. We will also introduce you to the
workings of Maya, a 3D software package. We will demonstrate the workings of
the Maya Personal Learning Edition—a (free) download is required.
viii PREFACE
Appendices
In Appendix A, you will find detailed instructions on how to install the OpenGL
and GLUT libraries. Appendix B describes how to download, install the sample
code that is detailed in this book. You will also find details on how to compile
PREFACE ix
and link your code using the OpenGL libraries. Appendix C describes the Maya
PLE and how to download it.
The system requirements for running the examples in this book, as well as for
running Maya PLE are as follows:
Software Requirements
• Windows 2000 or higher
•C/C++ compiler such as Microsoft Visual Studio on Windows or GCC
(Gnu Compiler Collection) on Unix
x PREFACE
Hardware Requirements
• Intel Pentium II or higher/AMD Athlon processor
• 512 MB RAM
• Hardware-accelerated graphics card (comes standard on most systems)
Intended Audience
This book is aimed at undergraduate students who wish to gain an overview of
Computer Graphics. The book can be used as a text or as a course supplement
for a basic Computer Graphics course.
The book can also serve as an introductory book for hobbyists who would
like to know more about the exciting field of Computer Graphics, and to help
them decide if they would like to pursue a career in it.
Acknowledgments
The support needed to write and produce a book like this is immense. I would
like to acknowledge several people who have helped turn this idea into a reality,
and supported me through the making of it:
First, my husband, Rajesh Pai, who supported me through thick and thin. You
have been simply awesome and I couldn't have done it without your constant
encouragement.
A big thanks to my parents, Anuradha and Girjesh Govil, who taught me to
believe in myself, and constantly egged me on to publish the book.
Thanks to Carmela Bourassa of Alias software, who helped provide
everything I needed to make Maya come alive.
A very special thanks to my editor, Wayne Wheeler, who bore with me
through the making of this book and to the entire Springer staff who helped to
produce this book in its final form.
I would like to dedicate this book to my kids, Sonal and Ronak Pai, who
constantly remind me that there is more to life than CG.
PREFACE xi
CG technology is emerging and changing every day. For example, these days,
sub-division surfaces, radiosity, and vertex shaders are in vogue. We cannot
hope to cover every technology in this book. The aim of the book is to empower
you with the basics of CG-providing the stepping-stone to pick up on any CG
concept that comes your way.
A key tenet of this book is that computer graphics is fun. Learning about it
should be fun too. In the past 30 years, CG has become pervasive in every aspect
of our lives. The time to get acquainted with it is now—so read on!
Section I
The Basics
Imagine how the world would be if computers had no way of drawing pictures
on the screen. The entire field of Computer Graphics-flight simulators, CAD
systems, video games, 3D movies-would be unavailable. Computers would be
pretty much what they were in the 1960s - just processing machines with
monitors displaying text in their ghostly green displays.
Today, computers do draw pictures. It's important to understand how
computers actually store and draw graphic images. The process is very different
from the way people do it. First, there's the problem of getting the image on the
screen. A computer screen contains thousands of little dots of light called pixels.
To display a picture, the computer must be able to control the color of each pixel.
Second, the computer needs to know how to organize the pixels into meaningful
shapes and images. If we want to draw a line or circle on the screen, how do we
get the computer to do this?
The answers to these questions form the basis for this section. You will learn
how numbers written in the frame buffer control the colors of the pixels on the
screen. We will expose you to the concept of two-dimensional coordinate
systems and how 2D shapes and objects can be drawn and transformed in this 2D
world.You will learn the popular algorithms used to draw basic shapes such as
lines and circles on the computer.
These days, three-dimensional graphics is in vogue. As a reader, you too
must be eager to get on to creating gee-whiz effects using these same principles.
It is important, however, to realize that all 3D graphics principles are actually
extensions of their 2D counterparts. Understanding concepts in a 2D world is
much easier and is the best place to begin your learning. Once you have mastered
2D concepts, you will be able to move on to the 3D world easily. At every step,
you will also have the opportunity to implement the theory discussed by using
OpenGL.
At the end of the section, we shall put together everything we have learned
to develop a computer game seen in many video arcades today.
Chapter *I
From Pixels to Shapes
The fundamental building block of all computer images is the picture element,
or the pixel. A pixel is a dot of light on the computer screen that can be set to
different colors. An image displayed on the computer, no matter how complex,
is always composed of rows and columns of these pixels, each set to the
appropriate color and intensity. The trick is to get the right colors in the right
places.
Since computer graphics is all about creating images, it is fitting that we
begin our journey into the computer graphics arena by first understanding the
pixel. In this chapter, we will see how the computer represents and sets pixel
colors and how this information is finally displayed onto the computer screen.
Armed with this knowledge, we will explore the core graphics algorithms used
to draw basic shapes such as lines and circles.
In this chapter, you will learn the following concepts:
What pixels are
w How the computer represents color
How the computer displays images
w The core algorithm used to draw lines and circles
w How to use OpenGL to draw shapes and objects
1.1 Computer Display Systems
The computer display, or the monitor, is the most important device on the
computer. It provides visual output from the computer to the user. In the
Computer Graphics context, the display is everything. Most current personal
computers and workstations use Cathode Ray Tube (CRT) technology for their
displays.
As shown in Fig. 1.1, a CRT consists of
An electron gun that emits a beam of electrons (cathode rays)
A deflection and focusing system that directs a focused beam of
electrons towards specified positions on a phosphorus-coated screen
A phosphor-coated screen that emits a small spot of light proportional
to the intensity of the beam that hits it
The light emitted from the screen is what you see on your monitor.
Phosphor
coated screen
The point that can be lit up by the electron beam is called a pixel. The
intensity of light emitted at each pixel can be changed by varying the number of
electrons hitting the screen. A higher number of electrons hitting the screen will
result in a brighter color at the specified pixel. A grayscale monitor has just one
phosphor for every pixel. The color of the pixel can be set to black (no electrons
hitting the phosphor), to white (a maximum number of electrons hitting the
phosphor), or to any gray range in between. A higher number of electrons hitting
the phosphor results in a whiter-colored pixel.
A color CRT monitor has three different colored phosphors for each pixel.
Each pixel has red, green, and blue-colored phosphors arranged in a triangular
group. There are three electron guns, each of which generates an electron beam
to excite one of the phosphor dots, as shown in Fig.l.2. Depending on the
monitor manufacturer, the pixels themselves may be round dots or small squares,
as shown in Fig. 1.3.
Electron Guns
el composed of a triad
The light on the screen generated by the beam of electrons in our CRT fades
quickly-in 10 to 60 microseconds. In order to keep a picture on the screen for
a while, the picture needs be redrawn before it disappears off the screen. This is
called refreshing the screen. Most display systems use raster scan technology to
perform the refresh process. In this technology, the electron beam is directed
discretely across the screen, one row at a time from left to right, starting at the
upper left corner of the screen. When the beam reaches the bottommost row, the
process is repeated, effectively refreshing the screen.
Raster scan systems use a memory buffer called frame buffer (or refresh
buffer) in which the intensities of the pixels are stored. Refreshing the screen is
performed using the information stored in the frame buffer. You can think of
frame buffer as a two dimensional array. Each element of the array keeps the
Fig.l.5: Monochrome display: frame buffer for turning pixels on and off
intensity of the pixel on the screen corresponding to that element.
For a monochrome display, the frame buffer has one bit for each pixel. The
display controller keeps reading from the frame buffer and turns on the electron
gun only if the bit in the buffer is as shown in Fig. 1.5.
Systems can have multiple buffers. Foreground buffers draw directly into the
window specified. Sometimes a background buffer is also used. The background
buffer is not displayed on the screen immediately. We shall talk about buffering
modes in more detail when we study animation.
INDEX
\
Index value Final Pixel color
displayed
Color Table
of referring to colors is called RGB mode. We shall employ the RGB mode to
refer to color throughout the rest of this book.
We have seen how pixel colors are stored and displayed on the screen. But
we still need to be able to identify each pixel in order to to set its color. In the
next section, we shall see how to identify individual pixel points that we want to
paint.
1.3 Coordinate Systems: How to IdentifL Pixel Points
values of x andy at the intersections completely define the position of this point.
In the Cartesian coordinate system, we label this point as (x,y). x and y are called
the coordinates of the point (and could have a negative value). In this system, the
origin is represented as (0,0), since it is at 0 distance from itself. A coordinate
system can be attached to any space within which points need to be located.
Coming back to the world of computers, recall that our computer display is
represented physically in terms of a grid of pixels. This grid of pixels can be
defined within its own Cartesian coordinate system. Typically, it is defined with
an origin at the upper left corner of the screen. We refer to this Cartesian space
as the physical coordinate system. Within this system, each pixel can then be
uniquely identified by its (x,y) coordinates, as shown in Fig.l.8.
Fig.l.8: Identifying pixels on the screen
Now, consider an applicatibn window being shown on this display. We can
specify a Cartesian space within which the application resides within. In Fig. 1.9,
the x- coordinates of the window define a boundary ranging from -80 to 80 and
the y-coordinates fiom -60 to 60. This region is called the clipping area, and is
also referred to as the logical or world coordinate system for our application.
This is the coordinate system used by the application to plot points, draw lines
and shapes, etc. Objects within the clipping area are drawn, and thosc outside
this area are removed or clipped from the scene. The clipping area is mapped
onto a physical region in the computer display by mapping the application
boundaries to the physical pixel boundaries of the window.
If the clipping area defined matches the (physical) resolution of thc window,
then each call to draw an (x,y)point (with integer values) in the world coordinate
system will have a one-to-onc mapping with a corresponding pixel in the
physical coordinate system.
For most applications, the clipping area does not match the physical size of
the window. In this case, the graphics package needs to perform a transformation
Clipping area:
320 by 240 units
(world coordinates)
0
0
0
0
8
clipping area:
160 by 120
(in world coordinates)
Fig.l.11: Viewport of a Window
Example Time
Let us run our first OpenGL program to get a handle on some of the concepts we
have just learned. For information on OpenGL and GLUT and how to install it
on your system refer to Appendix A on details. For information on how to
download the sample example code from the Internet and how to compile and
link your programs, refer to Appendix B.
The following example displays a window with physical dimensions of 320
by 240 pixels and a background color of red. We set the clipping area (or the
world coordinates) to start from (0,O) and extend to (160,120). The viewport is
set to occupy the entire window, or 320 by 240 pixels. This setting means that
every increment of one coordinate in our application will be mapped to two pixel
increments in the physical coordinate system (application window boundaries
(0,O) to (160,120) vs. physical window boundaries (0,O) to (320,240)). If the
viewport had been defined to be only 160 by 120 pixels, then there would have
been a one-to-one mapping from points in the world coordinate space to the
physical pixels.
However, only one fourth of the window would have been occupied by the
application! Depending on where you install the example files, you can find the
source code for this example in: Examplel-l/Examplel-1. cpp.
void Displaylvoid)
{
llclear all pixels with the specified clear color
glClear(GL-COLOR-BUFFER BIT);
Ildonlt wait, start flushing OpenGL calls~odisplay buffer
glFlush0;
1
void initlvoidl{
llset the clear color to be red
glClearColor(1.O,O.OIO.O,l -0);
llset the viewport to be 320 by 240, the initial sii of the window
glViewport(0,0,320,240~;
11set the 2D clipping area
gluOrtho2D(O.O, 160.0,0.0, 120.01;
1
void mairdint argc, char* argv01
{
glutlnitDisplayMode(GLUT-SINGLE I GLUT-RGB);
glutlnitWindowSi 1320,240);
glutCreateWindowVMy first OpenGL Window");
init0;
glutDisplayFunclDisplay1;
glutMainLoop0;
1
Since this is our first OpenGL program, Let us understand the example line by
line.
Anclude cwindows.h>
Anclude cgl\glut.h>
The wind0ws.h is required by all windows applications. The header file g1ut.h
includes the GLUT library functions as well as g1.h and glu.h, the header files for the
OpenGL library functions. All calls to the glut library functions are prefixed with
glut. Similarly, all calls to the OpenGL library functions start with the prefix gl or glu.
The Body
Let us look at the main program first. The line
glutlnitDisplayMode1GLUT-SINGLE I GLUT-RGB);
tells the GLUT library what type of display mode to use when creating the
application window. In this case, we specify that we will be using only a single
frame buffer (GLUT-SINGLE) and we want to specify colors in the RGB color
mode (GLUT-RGB). We will discuss more about frame buffers in a later
chapter.
The next call
initializes the window to have an initial size (physical resolution) of 320 by 240
pixels.
The call
actually creates the window with the caption "My First OpenGL Window".
The next function
registers the callback function for display redrawing to be the function Display.
The Display callback is triggered whenever the window needs to be redrawn, and
GLUT will automatically call the Display function for you. When does the
window need to be redrawn? When you first display the window, when you
resize the window, or even when you move the window around. We shall see
what the init function and the Display function actually do in a bit.
Finally, we make a call to the glut library function
This function simply loops, monitoring user actions and making the necessary
calls to the specified callback functions (in this case, the 'Display' function) until
the program is terminated.
This gl library command sets the color for clearing out the contents in the h m e buffer
(which then get drawn into the window). It expects the RGB values, in that order, as
parameter,^ as well as the alpha component of the color. For now, we set the alpha to
always be 1.The above command will set the clear color to be pure red. Try experimenting
with different clear colors and see what effect this has on the window display.
Next, we define the viewport to be equal to the initial size of the window by
calling the function
And we set the clipping area, or our world coordinate system, to be (0,O) to
(160,120) with the glu library command
On a computer, the memory (frame buffer) holding the picture is usually filled
with the last picture you drew, so you typically need to clear it with some
background color before you start to draw the new scene.
OpenGL provides glClear as a special command to clear a window. This
command can be much more efficient than a general-purpose drawing command
since it clears the entire frame buffer to the current clearing color. In the present
example, we have set the clear color earlier to be red.
In OpenGL, the frame buffer can be further broken down into buffers that
hold specialized information.
The color buffer (defined as GL-COLORBUFFERBIT) holds the color
information for the pixel. Later on, we shall see how the depth buffer holds depth
information for each pixel.
The single parameter to glClear() indicates which buffers are to be cleared.
In this case, the program clears only the color buffer.
Finally, the hnction
forces all previously issued OpenGL commands to begin execution. If you are
writing your program to execute within a single machine, and all commands are
truly executed immediately on the server, glFlush() might have no effect.
However, if you're writing a program that you want to work properly both with
and without a network, include a call to @Flush() at the end of each frame or
scene. Note that glFlush() doesn't wait for the drawing to complete -it just
forces the drawing to begin execution.
Voila: when you run the program you will see a red window with the caption
"My First OpenGL window". The program may not seem very interesting, but it
demonstrates the basics of getting a window up and running using OpenGL.
Now that we know how to open a window, we are ready to start drawing into it.
Plotting Points
Objects and scenes that you create in computer graphics usually consist of a
combination of shapes arranged in unique combinations. Basic shapes, such as
points, lincs, circles, etc., are known as graphics primitives. The most basic
primitive shape is a point.
In the previous section, we considered Cartesian coordinates and how we can
map the world coordinate system to actual physical scrccn coordinatcs. Any
point that we define in our world has to be mapped onto the actual physical
screen coordinates in order for the correct pixel to light up. Luckily, for us,
OpenGL handles all this mapping for us; we just have to work within the extent
of our defincd world coordinate systcm. A point is reprcscntcd in OpcnGL by a
set of floating-point numbers and is called a vertex.
Wc can draw a point in our window by making a call to the gl library function
The {2,3,4) option indicates how many coordinates define the vertex and the
{s,i,d,f) option defines whether the arguments are short, integers, double
precision, or floating-point values. By default, all integer values arc internally
converted to floating-point values.
For cxamplc, a call to
#include cwindows.h>
Anclude <gRglut.h>
void Display(void1
llclear all piiels with the specified clear color
glClear(GL-COLOR-BUFFER-BIT);
lldraw the four points in four colors
glBeginlGL-POINTS);
glColor3f(O.O, 1.O, 0.0); I1green
gNertex2fll O.,10.1;
glColor3f(l.Or 1.0,O.O); I1yellow
glVertex2f(lO.,ll0.);
glColor3fl0.0, 0.0, 1.Dl; I1blue
gNertex2fU 50.,110.1;
glColor3fU.Or 1.0, 1.0); I1white
glVertex2f~l50.,10.);
glEnd0;
The parameter defines the size in pixels of the points being drawn. A 5 pixel
point is large enough for us to see it without squinting our eyes too much!
In this function, we also we set the clear color to be red. The Display function
defines all the drawing routines needed
The function call
sets the color for the next openGL call. The parameters are, in order, the red,
green, and blue components of the color. In this case, we redefine the color
before plotting every vertex point. We define the actual vertex points by calling
the function
with the appropriate (x,y) coordinates of the point. In this example, we define
two callback functions. We saw how to define the callback function for
redrawing the window. For the rest of the book, we will stick with the convention
of this function being called "Display".
In this example we define the viewport and clipping area settings in a new
callback function called reshape. We register this callback function with
OpenGL with the command
This means that the reshape function will be called whenever the window resizes
itself (which includes the first time it is drawn on the screen!) The function
receives the width and height of the newly shaped window as its arguments.
Every time the window is resized, we reset the viewport so as to always cover
the entire window. We always define the world coordinate system to remain
constant at ((0,0),(160,120)). As you resize the window, you will see that the
points retain their distance from the corners. What mapping is being defined? If
we change the clipping area to be defined as
you would see that the points maintain their distance from each other and not
from the corners of the window. Why?
1.4 Shapes and Scan Converting
We are all familiar with basic shapes such as lines and polygons. They are easy
enough to visualize and represent on paper. But how do we draw them on the
computer? The trick is in finding the right pixels to turn on!
The process by which an idealized shape, such as a line or a circle, is
transformed into the correct "on" values for a group of pixels on the computer is
called scan conversion and is also referred to as rasterizing.
Over the years, several algorithms have been devised to make the process of
scan converting basic geometric entities such as lines and circles simple and fast.
The most popular line-drawing algorithm is the midpoint-line algorithm.
This algorithm takes the x- and y- coordinates of a line's endpoints as input and
then calculates the x,y-coordinate pairs of all the pixels in between. The algo-
rithm begins by first calculating the physical pixels for each endpoint. An ideal
line is then drawn connecting the end pixels and is used as a reference to
determine which pixels to light up along the way. Pixels that lie less than 0.5
units from the line are turned on, resulting in the pixel illumination as shown in
Fig.1 .l2.
To tell OpenGL what shape you want to create with the specified vertices, you
bracket each set of vertices between a call to glBegin() and a call to glEnd(). The
1) A line is defined by 2 vertices 2) A triangle is defined by 3 vertices
GL-LINE-STRIP
- - ---- series-of-connected-lines
p
GL-TRIANGLES --- ----
r
strip---
of linked--triangles-- -
- - -
GL-POLY
- -
GON -- - -
vertices- define--a simple convex
-- -- polygon__I
1
Note that primitives are all straight-line primitives. There are algorithms like the
midpoint algorithm that can scan convert shapes like circles and other hyperbolic
fig.s. The basic mechanics for these algorithms are the same as for lines: figure
out the pixels along the path of the shape, and turn the appropriate pixels on.
Interestingly, we can also draw a curved segment by approximating its shape
using line segments. The smaller the segments, the closer the approximation.
For example, consider a circle. Recall from trigonometry that any point on a
circle of radius r (and centered at the origin) has an x,y-coordinate pair that can
be represented as a function of the angle theta the point makes with the axes, as
shown in Fig. 1.14.
As we vary theta from 0 to 360 degrees (one whole circle), we can get the
(x,y) coordinates of points along the circle. So if we can just plot "enough" of
these points and draw line segments between them, we should get a fig. that
looks close enough to a circle. A sample code snippet to draw a circle with
approximately 100 points is shown below. Note that we need to add the center of
the circle to our equations to position our circle appropriately.
-
GLint circlegoints 100;
void MyCircle2flGLfloat centerx, GLfloat centery, GLfloat radius){
GLint i;
GLdouble theta;
glBeginIGL-POLYGON);
-
for (i 0;i < circlegoints; i+ + 1 {
theta- 2"Pl"ilcirclegoints; 11 angle in radians
glVertex2f(centerx+ radius"cosltheta1,
centery + radius"sin(theta1l;
Remember that the math functions cos and sin require angles in radians and
that 2PI radians make 360 degrees-hence our conversions in the code. We can
construct more complex shapes by putting these basic primitives together.
Shown below is a snippet of code to draw a stick figure of a person as shown in
Fig.1.I5 Stick Figure
the Fig. 1.15. The figure is composed of lines, polygons, points and a circle. The
entire code can be found in Examplel-3/Examplel-3.cpp.
I1Example 1-3.cpp
void Displaylvoid)
{
llclear all pixels with the specified clear color
glClearlGL-COLOR-BUFFER-BIT);
glColor3fll.0,0.8,0.1 I;
MyCircle2f180.,85., 10.);
I1polygonal body
glC0lor3fl0.8~0.0~0.9);
glBeginlGL-POLYGON);
glVertex2fl75.,75.);
gNertex2ff85.,75.);
glVertex2R100.,30.1;
gNertex2f160.,30.1;
glEndO;
llrectangular legs
glColor3fll.0,0.8,0.1);
glRectfl70.,5.,75.,30.);
glRectf185.,5.,90.,30.1;
I1 but lines for hands!
glBeginIGL-LINES);
glVertex2f 174.,70.1; gNertex2f 150.,50.);
glEnd0;
glBeginlGL-LINES);
glVertex2f (86.,70.1; glVertex2f (110.,50.1;
glEnd0;
Note that with OpenGL, the description of the shape of an object being drawn
is independent of the description of its color. Whenever a particular geometric
object is drawn, it is drawn using the currently specified coloring scheme. Until
the color or coloring scheme is changed, all objects are drawn in the current
coloring scheme. Similarly, until the point or line sizes are changed, all such
primitives will be drawn using the most currently specified size. Try composing
your own objects by putting smaller primitives together.
If you run the above program, you may notice that the slanting lines appear
to be jagged. This is an artifact caused due to the algorithms that we employ to
rasterize the shapes and is known as aliasing.
An ti-Aliasing
The problem with most of the scan conversion routines is that the conversion is
jagged. This effect is an unfortunate consequence of our all or nothing approach
to illuminating pixels. At high resolutions, where pixels are close together, thi
effect is not noticeable, but on low-resolution monitors, it produces a harsh,
jagged look. Aliasing can be a huge problem in computer generated movies,
when you can sometimes actually see jagged lines crawling from scene to scene,
creating a disturbing effect.
A solution to this problem is called anti-aliasing. It employs the principle that
if pixels are set to different intensities, and if adjoining pixel intensities can be
properly manipulated, then the pixels will blend to form a smooth image. So
going back to the midpoint algorithm, as shown in Fig. 1.16, anti-aliasing would
turn pixels on with varying intensities (depending on how a one-unit thick line
would intersect with the pixels), instead of merely turning pixels on and off. This
process tends to make the image look blurry but more continuous.
1) An ideal line using end pixels 2) Illuminating the pixels 3) Exaggerated view
of jaggp line
glEnable (GL-LINE-SMOOTH);
glEnable (GL-BLEND);
glBlendFunc(GL-SRC-ALPHA,GL-ONE-MINUS-SRC-ALPHA);
glHintlGL-LINE-SMOOTH-HINT I GL-POLYGON-SMOOTH-HINT, GL-DONT-CARE);
Summary
In this chapter, we have covered most of the groundwork to understand the work-
ings of the computer and how the computer stores and displays simple graphics
primitives. We have discussed the color model and how OpenGL employs the
RGB mode to set pixel colors. We have also seen how to identify pixel
coordinates and light up different points on the computer window using
OpenGL. Finally we have learned how basic shapes are rasterized, anti-aliased
and finally displayed on a grid of pixels. In the next chapter, we will explore how
to construct and move the shapes around in our 2D world.
Chapter 2
Making Them Move
In the previous chapter, we saw how to draw basic shapes using the OpenGL
graphics library. But we want to be able to do more than just draw shapes: we
want to be able to move them around to design and compose our 2D scene. We
want to be able to scale the objects to different sizes and orient them differently.
The fbnctions used for modifying objects such as translation, rotation, and
scaling are called geometric transformations.
Why do we care about transformations? Usually we define our shapes in a
coordinate system convenient to us. Using transformation equations enables us
to easily modify object locations in the world coordinate system. In addition, if
we transform our objects and play back the CG images fast enough, our eyes will
be tricked to believe we are seeing motion. This principle is used extensively in
animation, and we shall look into it in detail in Chapter 7. When we study 3D
models, we will also see how we can use transformations to define hierarchical
objects and to "transform" the camera position.
This chapter introduces the basic 2D transformations used in CG: translation,
rotation, and scaling. These transformations are essential ingredients of all
graphics applications.
In this chapter you will learn the following concepts:
Vectors
A vector is a quantity that has both direction and length. In CG, a vector
represents a directed line segment with a start point (its tail) and an end point (the
head, shown typically as an arrow pointed along the direction of the vector). The
length of the line is the length of the vector.
coordinates of the points individually. What this means is that to get from P to
Q, we shift right along the x-axis by two units and down the y-axis by one unit.
Interestingly, any point P1 with coordinates (x,y) corresponds to the vector VPI,
with its head at (x,y) and tail at (0,O) as shown in Figure 2.1. That is, there is a
one-to-one correspondence between a vector, with its tail at the origin, and a
by the vector [;i
point on the plane. This means that we can also represent the point P(x,y)
Subtraction follows easily as the addition of a vector that has been flipped:
that is VI-VZ= VI+(-V2).
Matrices
A matrix is an array of numbers. The number of rows (m) and columns (n) in the
array defines the cardinality of the matrix (m x n). In reality, a vector is simply a
matrix with one column (or a one-dimensional matrix). We saw how displays are
just a matrix of pixels. A frame buffer is a matrix of pixel values for each pixel
on the display.
Matrices can be added component wise, provided they have the same cardinality:
all
[a21
a12
+ 1:: b12
b2,]=
a l l + b l l a12+b12
a22b2J
Multiplication of a matrix by a scalar is simply the multiplication of its compo-
nents by this scalar:
Two matrices can be multiplied if and only if the number of columns of the first
matrix (m x n) is equal to the number of rows of the second (n xp). The result is
calculated by applying the dot product of each row of the first matrix with each
:I
column of the second. The resultant matrix has a cardinality of (m x p).
b l l b12
-
-
b21 b22
b3 1 b32
a l l * b l l + a12*b21 + a12*b31
a21*b11 + a22*b21 + a23*b31
all*b12 + a12*b22 + a13*b32
a21*b12 + a22*b22 + a23*b32 1
We can multiply a vector by a matrix if and only if it satisfies the rule above.
a l l a12 a13
Whew! After that fairly exhaustive review of matrices and vectors, we are ready
to get into the thick of transformations.
The functions used for modifying the size, location, and orientation of objects or
of the camera in a CG world are called geometric transformations.
Transformations are applied within a particular space domain, be it an object
space or the camera space or a texture space. We shall mainly be discussing
transformations applied in the object space, also called object transformations or
model transformations. The mathematics for transformations in other spaces
remains the same.
Object Space
Usually, objects are defined in a default coordinate system convenient to the
modeler. This coordinate system is called the object coordinate system or object
space.
Y
z I
Fig.2.5: Object space
The object coordinate system can be transformed, in order to transform the
object within the world coordinate system. An object transformation sets the
state of the object space. Internally, this state is represented as a matrix. All the
objects drawn on the screen after this state is set are drawn with the new
transformations applied. Transformations make it convenient to move objects
within the world, without actually having to model the object again!
Object
Coordinate
System
Coordinate
System
Fig.2.6: Object space is transformed within the world coordinate system.
Let us look into the three kinds of transformations most commonly used
CG: translation, rotation, and scaling.
[:,I
If we define the vector for a given points P(x,y) as:
-
GLint circlegoints 100;
void MyCircle2flGLfloat centerx, GLfloat centery, GLfloat radius){
GLint i;
GLdouble angle;
glBeginlGL-POLYGON);
-
for (i 0; i < circlegoints; i+ + 1 {
-
angle Z*Pl*ilcircle~oints; I1 angle in radians
glVertex2flcenterx+radius*coslangle),
centery + radius*sin(angle));
1
glEnd0;
1
In Example2-1, we use this function to draw a brown ball centered at the
origin with the radius of the ball set to be 15 units in our world coordinate
system:
GLfloat RadiusOfBall - 15.;
11 Draw the ball, centered at the origin
void draw-ball0 {
glColor3f10.6,0.3,0.1;
MyCircle2flO.,O.,RadiusOfBalll;
1
We want to bounce this ball up and down in our display window. That is, we
wish to translate it along the y-axis. The floating-point routine for performing
translations in OpenGL is
It accepts three arguments for translation along the x-, y-, and z-axes
respectively. The z-axis is used for 3D worlds, and we can ignore it for now by
setting the Tz value to be 0.
Before we apply a new translation, we need to set the transformation state in
the object space to be an identity matrix (i.e., no transformation is being applied).
The command
clears out the current transformation state with the identity matrix. We do this
because we do not wish to reuse old transformation states.
Then, we constantly add (or subtract) to the ty component along the y-axis,
and draw the ball in this newly transformed position. The snippet of code to
translate an object in a window with the maximum extent of the world
coordinates set to (160,120) is shown below. A ydir variable is used to define
whether the ball is bouncing up or down (and hence whether we should
increment or decrement ty).
Camera (or viewing) and object transformations are combined into a single
matrix in OpenGL, called the model-view matrix (GL-MODELVIEW). The
command
specifies the space where the transformations are being applied. The mode is
normally set in the reshape function where we also set the clipping area of the
window.
That is, whenever the window is reshaped, resized, or first drawn, we specify
any further transformations to be applied in the object space.
If you try to view this animation, we shall see motion that is jerky, and you
may even see incomplete images being drawn on the screen. To make this
bouncing motion appear smooth, we make use of a concept called double
buffering.
Double Buffering
Double buffering provides two frame buffers for use in drawing. One buffer, called
the foreground buffer is used to display on the screen. The other buffer, called the
background buffer, is used to draw into. When the drawing is complete, the two
buffers are swapped so that the one that was being viewed is now being used for
drawing and vice versa. The swap is almost instantaneous. Since the image is
already drawn when we display it on screen, it makes the resulting animation look
smooth, and we don't see incomplete images. The only change in the required to
activate double buffering is to specify the display mode to be GLUT-DOUBLE.
will cause the two buffers to be swapped. After they are swapped, we need to
inform OpenGL to redraw the window (using the new contents of the foreground
buffer).
The function
glutPostRedisplay0
forces a re-draw of the window.
void Displaylvoid)
{
glClearlGL-COLOR-BUFFER-BIT);
glClearlGL-COLOR-BUFFER-BIT);
11I 6 0 is max X value in our world
11Define X position of the ball to be at center of window
-
xpos 80.;
11120 is max Y value in our world
11set Y position to increment 1.5 times the direction of the bounce
-
ypos ypos+ydir "1.5;
11If ball touches the top, change direction of ball downwards
if lypos -- - 120-RadiusOfBall)
ydir -1;
11If ball touches the bottom, change direction of ball downwards
else if lypos 4adiusOfBall)
-
ydir 1;
llreset transformation state
glLoadldentii0;
11apply the translation
glTranslateflxpos,ypos, 0.);
I1draw the ball with the current transformation state applied
draw-ball0;
11swap the buffers
glutSwapBuffers0;
11force a redraw using the new contents
glutPostRedisplay0;
1
The entire code for this example can be found under Exarnple2-I/Example2_I.cpp
Scaling
An object can be scaled (stretched or shrunk) along the x- and y- axis by
multiplying all its points by the scale factors Sx and Sy. All points P=(x,y) on the
scaled shape will now become P' = (x',y') such that x' = Sx.x, y' = Sy.y. In matrix
In Figure 2.8, we show the circular shape being scaled by (112,2) and by (2,112).
Notice from the figure that scaling changes the bottom (base) position of the
shape. This is because the scaling equation we defined occurs around the origin
of the world coordinate system. That is, only points at the origin remain
unchanged. Many times it is more desirable to scale the shape about some other
predefined point. In the case of a bouncing ball, we may prefer to scale about the
bottommost point of the ball because we want the base of the shape to remain
unchanged. In a later section, we shall see how to scale the shape about a
predefined position.
Let us go back to our bouncing ball example. When the ball hits the ground
we want it to squash on impact and then stretch back up again into its original
shape. This is what we expect from a real (soft) ball bouncing on the ground. The
floating point command to scale an object is
which accepts three arguments for scaling along the x-, y- and z-axes. Scaling has
no effect when Sx=Sy=Sz=l. For now, we will set the Sz to be 1.
We can modify the Display code from the previous example to include
scaling of the ball. When the ball hits the ground, and for some time afterwards,
we stop translating and squash the shape. That is, we scale down the shape along
the y-axis and proportionately scale up along the x-axis. When we reach a
predetermined squash, we stretch back up again and restart the translation.
Shown below is the code to perform this transformation.
11 Shape has hit the ground! Stop moving and start squashing down and then back up
if (ypos- -sy - syesquash ; - -
RadiusOfBall && ydir -1 1 {
a (SY < 0.81
11 reached maximum squash, now un-squash back up
squash 1.1;
else if (sy > 1.I{
-
11reset squash parameters and bounce ball back upwards
- -
sy 1.;
squash 0.9;
-
ydir 1;
1
sx - 1.lsy;
1
11 120 is max Y value in our world
11set Y position to increment 1.5 times the direction of the bounce
else {
- --
ypos ypos+ ydir *1.5 - (1.-syl*RadiusOfBall;
if lypos 120-RadiusOfBalll
ydir -1; -
else if (ypos <RadiusOfBalll
ydir 1; -
1
glLoadldentity(1;
glTranslatef(xpos,ypos, 0.1;
glScalef(sx,sy, 1.I;
draw-ball();
The entire code can be found under Example 2_2/Exarnple 2-2.cpp. Notice
that two transformations are applied to the object-translation and scaling.
When you run the program, you will notice that the ball doesn't stay on the
ground when it squashes! It seems to jump up. This happens because the scaling
is happening about the origin-which is the center of the ball!
Rotation
A shape can be rotated about any of the three axes. A rotation about the z-axis
will actually rotate the shape in the xy-plane, which is what we desire in our 2D
world. The points are rotated through an angle about the world origin as shown
in Fig.2.9. Mathematically, a rotation about the z-axis by an angle 0 would result
in point P (x,y) transforming to P' (x',y') as defined below:
In matrix form:
or P=R.P
Fig.2.9: Rotation of shape
Where R is the rotation matrix shown abovc. Positive angles are measured
counterclockwise from x toward y. Just like scaling, rotation occurs about the
world origin. Only points at the origin are unchanged after the rotation
transformation.
It expects the angle of rotation in degrees and a nonzero vector value about
which you wish the rotation to occur. For rotations in the xy-plane, we would set
vx=vy=O and vz =1 (i.e., the unit vector along the z-axis)
Let us go back to our bouncing ball example. Of course, rotating a round ball
will have no effect on the ball. Let us redefine the draw-ball routine to draw an
clongated ball.
11 Draw the ball, centered at the origin and scaled along the X axis
11 It's a football!
void draw ball0 {
gl~olor3f(0.6,0.3,0.~;
glScalef(l.3,I .,.I;I
MyCircle2f(O.,O.,RadiusOfBalI);
1
To rotatc the ball while it's bouncing, we add thc following lines of code to
our Display function:
rot - rot+2.;
I/ reset rotation after a full circle
--
if (rot > 3601
rot 0;
glLoadldentity0;
glTranslateflxpos,ypos, 0.1;
glScalef(sx,sy, 1.I;
glRotateflrot, 0.,0.,1 .I;
draw-ball0;
14
To go from homogenous coordinates back to our original non-homogenous
world, we just divide the coordinates by W. So the homogenous point
represented by:
I;[
is equal to the point (x/K y/I+').
Two points of homogenous coordinates (x,y, W) and (x ',y ', W') are the same point
if one is a multiple of the other. So, for example, (1,2,1) and (2,4,2) are the same
points represented by different coordinate triples. The points with W=O are points
at infinity and will not appear in our discussions.
Because 2D points are now three elcmcnt column vcctors, transformation
matrices used to transform a point to a new location must be of cardinality 3x3.
In this system, the translation matrix T is defined as
Any point P (x,y,W) that is translated by the matrix T results in the
transformed point P' defined by the matrix-vector product of T and P:
Satisfy yourself that for W=l this is consistent with what we learned earlier.
For any other value of W, convert the coordinates to their non-homogenous form
by dividing by W and verify the result as well.
S=
That is, the matrix product of the two translations produces the desired
transformation matrix. This matrix product is referred to as the concatenation or
composition of the two transformations. Again, please verify for yourself that
this is indeed the case. It can be proven mathematically that applying successive
transformations to a vertex is equivalent to calculating the product of the
transformation matrices first and then multiplying the compounded matrix to the
vertex [FOLE95]. That is, we can selectively multiply the fbndamental R, S and
T matrices to produce desired composite transformation matrices.
The basic purpose of composing transformations is to gain efficiency. Rather
than applying a series of transformations one after another, a single composed
transformation is applied to the points on our object. At any given time, the
current transformation state is represented by the composite matrix of the applied
transformations.
Let us apply the composition principle to the ball shape under discussion. Let
us go back to Exarnple2-2, where we were squashing the ball upon contact with
the ground. Remember that the ball seemed to jump when we squashed it. To
avoid this jump, we wish to scale the ball about its bottom most point. In other
words, we wish the bottommost point to stay at the same place when we squash
the ball.
I
I
I
I
I
I Fix
T1
l'l(xl,~l)
--
Fig.2.10: Translate shape by T I
From our study of transformations, we know how to scale the ball about the
world origin (which is where the center of the ball is located). To transform the
ball about its base, we can convert this problem into three basic transformations:
First, move the ball so that the desired fixed point of scaling (the base in this
I pl (xl,yl)
Fig.2.11: Scaling the translated shape
case) point Pl (XI,yl) is at the origin, by applying translation matrix TI. Then we
scale the shape as usual by applying a scaling transform S. The scaling occurs
about the origin, which is now where the base of the ball is positioned.
Finally, we translate the shape back such that P1 returns to its original point,
i.e. we apply a translation transform T2 = -TI. Viola: we have scaled the shape
keeping the bottommost point P1 (x1,yl) fixed. Effectively, we have scaled the
shape about the pivot point P I . Rotating a shape about a point works on the same
principle. First translate the shape so that the point of rotation is at the origin.
Apply the rotation and translate the shape back to its original location.
allows you to load a matrix directly to represent the current transformation state.
The command
multiplies the current transformation matrix by the specified matrix, M. Each
successive glMultMatrix*() or transformation command multiplies a new 4 x 4
matrix M to the current transformation matrix C to yield the (new current)
composite matrix C.M. In the end, vertices v are multiplied by the current matrix
to yield the transformed vertex locations. This process means that the last
transformation command called in your program is actually the first one applied
to the vertices. For this reason, you have to specify the matrices in the reverse
order of the transformation applied.
Consider the following code sequence, which draws a single point using
three transformations:
glMatrixModeIGL-MODELVIEW);
glLoadldentity0;
glMultMatrixflT2); 11apply transformation T2
glMultMatrixfIS1; 11 apply transformation S
glMultMatrixf(T11; I1 apply transformation T1
glBeginlGL-POINTS);
glVertex3f(v); I1 draw transformed vertex v
glEnd0;
Remember, we do need to set the matrix mode to specify which space the
matrix operations are occurring within (in this case, the object space represented
by the modelview matrix). With this code, the modelview matrix successively
contains I, T2, T2 S, and finally T2.S.T1, where I represents the identity matrix.
The final vertex tiansformation applied is equal to: (TZ.(S.(TI.v))) - notice that
the transformations to vertex v effectively occur in the opposite order in which
they were specified.
Coming back to our example, we want to scale the ball about its base. To do
this, we translate the ball so that its base lies at the origin. This is done by
translating the ball upward by its radius. If we initially define the transformation
matrix T1 as the identity matrix
-
GLfloat T1[161 {1.,0.,0.,0.,\
O.#I .ro.ro.J
o.ro.ll .,O.#\
o.ro.ro.ll .}
then to attain the matrix to translate the ball upward by its radius, we can just
set
-
T I [I31 RadiusOfBall;
Defining T1 to be { I .,0.,0.,0.,\
0.,1 .#O.#O.#\
46 2 3 HOMOGENOUS
COORDINA~ES OF MATRIX
AND C~ILIPOSITION TRANSFORMA~IONS
If we define the scaling matrix S also to initially be the identity matrix, then to
squash the ball by sx and sy, S would be set as
-
S[Ol sx;
-
S[51 sy;
The transformation sequence for scaling and bouncing the ball would be as
follows:
-
T I [I31 -RadiusOfBall;
I/ Translate ball back to center
glMultMatrixf(T1 I;
S[Ol sx;-
S[51 sy;-
I/ Scale the ball about its bottom
glMultMatrixf(S1;
-
T I [I31 RadiusOfBall;
I/ Translate the ball upward so that it's bottom point is at the origin
glMultMatrixflT1);
draw-ball0;
Now you will see that the ball remains on the ground when it is squashed-
just as we wanted! The entire code can be found under Example 2-4.
An Easier Alternative
The three commands we learned earlier, namely, glTranslate, glScale, and
glRotate, are all equivalent to producing an appropriate translation, rotation, or
scaling matrix and then calling glMultMatrix*() with this matrix. Using the
above functions, the same transform sequence for the bouncing ball would be as
follows:
llreset transformation state
glLoadldenti0;
I1 retain current position
glTranslateflxpos,ypos, 0.1;
Summary
In this chapter, we have covered one of the most mathematical concepts in Com-
puter Graphics: transformations. Transformations are based on vector and matrix
math. The basic transformations are of three types: translation, scale, and
rotation. These can be composed together to create complex motions. Although
the actual matrix math for transformations can be fairly tedious, OpenGL
provides a number of functions to easily manipulate and apply transformations
to objects. In the next section, we shall extend the concepts of 2D
transformations learned here to the more general 3D case. This is when we will
really be able to appreciate the true power of transformations.
Chapter 3
Pixels, Images and
Image M e s
e BMP file
4
- Palette (indexed mode)
Color
Pixel data
Table 3.1: Parts of the BMP file
The first part of the file is the header. The header contains information about the
type of image (BM, for BMP), its size, and the position of the actual image data with
respect to the start of the file. The header is defined in a structure as shown below:
{
11 Not a bitmap file
fcloselfp);
return (NULL];
1
infosize - header.bf0ffBits
siieoflBITMAPFILE
-
HEADER);
fread(*info, 1, infosii, fp)
-
imgsize (*info)->bmiHeader.biSiielmage;
11 sometimes imagesiie is not set in files
if (imgsize -- -
01
imgsize ((*info)->bmiHeader.biWidth *
(*info)->bmiHeader.biBitCount + 71 18 *
abs(1"info)-> bmiHeader.biHeight1;
freadlpixels, 1, imgsize, fp);
The code required to save a BMP file given the pixel data, is analogous.
We have provided C code to read and save bmp files. It can be found in the
files: bmp.cpp and bmp.h under the directory where you installed the example
code.. The functions have the following signatures
extem GLubyte 'ReadBiimap(constchar "filename,BlTMAPINFO "info);
extem int SaveBiimap(constchar 'filename, BITMAPINFO 'info,GLubyte 'bits);
reads a block of pixels from the frame buffer and returns a pointer to the pixel
data-yes it returns a pixmap! The exact signature for the glReadPixels function is
The function returns the pixel data from the frame buffer, starting with the pixel
whose lower left corner is at location (x, y), into client memory pointed to be the
variable: pixels. glReadPixels returns values for each pixel
(x + i, y + j) for 0 <= i < width and 0 <= j < height.
Pixels are returned in row order from the lowest to the highest row and from
left to right in each row. Typically, one would like to read the entire content of
the frame buffer - as defined by the extents of the viewport.
format specifies the color mode of the pixmap. GL-COLOR-INDEX for color-
indexed pixels, GL-RGB for RGB pixels, and GL-BGR-EXT for RGB-mode based
BMP files (since BMP files reverses the order of the R,G and B components).
type specifies the bit size of each color component. GL-BYTE or
GL-UNSIGNED-BYTE is used for 8-bit values (which are used by RGB-mode
images), GL-BITMAP is used for one-bit values (monochrome images) etc.
Several other parameters control the processing of the pixel data before it is
placed into client memory. These parameters are set with three commands:
glPixelStore, glPixelTransfer, and glPixelMap.We shall not look into the details
of these calls here. Interested readers are encouraged to refer to SHRE03 for
more information. For your convenience, we have defined a function:
GLubyte *
ReadBitmapFromScreenlBITMAPINFO **info)
that reads the current frame buffer, constructs an appropriate BITMAPINFO, and
returns the constructed pixmap. This function is declared in the file: bmp.h and
defined in the file: bmp.cpp. It is useful (but not necessary) to look at the code to
understand how to setup OpenGL to read pixel values from the frame buffer.
Recall the stick figure we drew in Examplel-3. In Example3-I, we save the
stick figure to a BMP file called stick.bmp in the same directory as the
executable. The code to read and save the state of the frame buffer is as follows:
BITMAPINFO *info;
-
GLubyte "pixels ReadBitmapFromScreeni&info);
SaveBitmapi"stick.bmp", info,pixels);
These lines of code are to be called after all drawing operations are complete,
in order that the frame buffer is completely defined. The generated BMP file can
now be loaded into your Microsoft Paint or any other program for further editing.
The code to draw out the stick figure and save it to a file can be found under
Example3-I/Example3~l.cpp.You will need to compile and link it with the
bmp.cpp file that we have provided.
to take a bitmap and transfer its contents to the frame buffer for drawing to the
screen. The hnction
glDrawPixelsiGLSizei width, glSizei height, Glenum format, Glenum type, Ghroid * pixels)
where x and y are the world coordinates along the x- and y-axis. This position is
affected by any transformations we may apply to the current object state.
A point to note about bitmaps and pixmaps is that they are defined upside
down! You can specify a negative height to invert the image.
In Example3-2, we read in a 64 by 64-sized pixmap. The image has a black
background and a yellow colored smiley face.
void Displaylvoidl
{
glClearlGL COLOR-BUFFER-BIT);
gl~aster~os2f1100,~osl;
I1 Draw the loaded pixels
glDrawPixelslinfo-> bmiHeader.biWidth,
info->bmiHeader.biHeight,
GL BGR EXT, GL-UNSIGNED-BYTE, pixels);
I1 draw the ball;t th&ame Y position
draw-ballWO.,yposl;
lldont wait, start flushing opengl calls to display buffer
glFlush0;
glutSwapBuffers0;
11 120 is max Y value in our logical coordinate system
- -
ypos ypos + 0.1;
if (ypos > 120.1
-
ypos 0;
11 Force a redisplay
glutPostRedisplayl1;
1
Notice that we call the ball drawing code using its center as a parameter. This
approach ensures that we draw the ball with a translated center without using the
OpenGL transformation functions. The reason is that the function glRasterPos is
affected by transformations. This, in turn means that it can be hard to control the
exact location of our smiley face. We shall see techniques on how to handle this
issue in a later chapter. The main () function reads in thc dcsired bitmap.
glPixelZoom~x,yl
This function will scale up the image by the specified x and y factors.
CHAPTER3 PIXELS,IMAGES
AND IMAGE
FILES
transparent pixel
0 Overlay Buffer
Operation Value
0 AND 0 0
1 AND 0 0
1 AND 1 1
OORO 0
1OR0 1
10R1 1
0 XOR 0 0
1 XOR 0 1
1 XOR 1 0
NOT(0) 1
NOT(1)
- --- 0
Table 3.2: Logical operations
(destination). The resultant pixel values are saved back in the frame buffer. For
true color pixels, the operations are performed on the corresponding bits of the
two pixel values, yielding the final pixel value.
Logical operations are especially useful on bit-blt type machines. These
machines allow you to perform an arbitrary logical operation on the incoming
data and the data already present, ultimately replacing the existing data with the
results of the operation, all in hardware. Since this process can be implemented
fairly cheaply and quickly in hardware, many such machines are available. All
gaming hardware, such as Nintendo and Xbox, support these operations in
hardware for fast implementations of pixellbit copying and drawing.
In Fig. 3.4, we show the OR operator applied to two monochrome (single
bit) pixel values. The bits with value 1 are shown in a white color, and those with
value 0 are shown in black.
if (FIRSTDISPLAY) {
11 Only clear the background the first time
glClear(GL-COLOR-BUFFER-BIT);
-
FIRSTDISPLAY FALSE;
11 enable logical operations
glEnable(GL-COLOR-LOGIC-OP);
11 Set logical operation to XOR
glLogicOplGL-XOR);
) else {
11 XOR incoming values with pixels in frame buffer
11 the next two lines will erase the previous image drawn
glRasterPos2f(prevxIprevyl;
glDrawPixels(info->bmiHeader.biWidth,
info-> bmiHeader.biHeight,
GL-BGR-UCT, GL-UNSIGNED-BYTE, piielsl;
1
I1 the next two lines draws in a new (XORed) image
glRasterPos2f~xpos,ypos~;
glDrawPixelslinfo-> bmiHeader.biWidth,
info-> bmiHeader.biHeight,
GL-BGR-UCT, GL-UNSIGNED-BYTE, pixels);
An additional bonus: since the smiley face has a black background (recall
that black is the transparent color by default), the resultant display only draws
the face, not the entire rectangular image. The entire code can be found in
Example3-4/Example 3-4.cpp. Try using the background image of the Alps and
see whether the XOR operation results in a desirable output.
3.4 lmage Enhancements
The real value of saving images on the computer is image processing - what can
be done to the image once it is resident on the computer. Almost all production
studios use a processing tool to perform final image touchups, called post-
production, to enhance the quality of their images. These images can be from livc
action footage or from computer-generated imagery. Effects like blending,
compositing, and cleaning up pixel values are used routinely in productions. The
idea behind image processing is simple: manipulate the pixel information of the
given image using some mathematical functions and save the result in a new
image file. We discuss two techniques in this section, namely, compositing and
red-eye removal.
Refer to [PORT841 for information on other image processing techniques.
lmage Compositing
Compositing is the most widely used operation in the post-production of films,
commercials, and even TV broadcasts. The basic operation in image compositing
is to overlay one (usually nonrectangular) image on top of the other. Recall from
Exarnple3-2, when we drew the smiley face on top of the background, we
displayed the entire image, not just the face, as we would have liked. XORing
flips the colors, so that is not always a good solution either. Compositing to the
rescue!
Various techniques are used for compositing images. One popular technique
is the blue screen process. First, the subject is photographed in front of an evenly
lit, bright, pure blue background. Then the compositing process, whether
photographic or electronic, replaces all the blue in the picture with the
background image, known as the background plate. In reality, any color can be
used for the background, but blue has been favored since it is the complementary
color to flesh tone. The source and destination images can come from livc action
images or be computer generated. Jurassic Park employed compositing
extensively to composite CG-generated dinosaurs in front of real livc shots.
The essential compositing algorithm is as shown below. It designates Blue or
the desired color as transparent. The source image is copied on top of a defined
destination image as follows:
-
for y 1 to height
-
for x 1 to width
if image[x, y] o transparent pixel then
copy irnageh yl
else
leave the destination unchanged
Fig. 3.8: Cornpositing Algorithm
The only problem, of course, is that "transparent" color can't be used in the
source image. Another technique for compositing, being used more frequently
now makes use of a mask or stencil. This technique uses logical operations as
follows:
Create a mask of the image. Many production houses create the mask
as part of the alpha channel of the image. The mask essentially
identifies the desired areas of the source image.
AND the source with the mask (not always required).
AND the background with NOT of mask
OR the results to produce the final composite.
AND AND
We show below, sample code for compositing the smiley face on top of the
background image of the Alps. The mask was created as a BMP image using
Adobe Photoshop. The entire code can be found under Example3_5/Example
3-5.cpp.
/I First copy background image into frame buffer
glLogicOplGL COPY);
gl~aster~os2i(0,0);
glPielZoom(0.5,0.8);
glDrawPixelslbginfo->bmiHeader.biWidth, bginfo->bmiHeader.biHeight,
GL-BGR-EXT, GL-UNSIGNED-BYTE, bgpixels);
You will see a composite image of the smiley face on top of the Alps-just
what we wanted!
Red-Eye Treatment
Image enhancements are possible because we have access to the actual pixel
information of the image.-we can target specific pixels based on some criteria
and then change their values. You may have used a very common feature in
image processing tools-red-eye removal.
Let us see how we can implement this feature for ourselves.
In Example3-6, we load in a smiley face with red eyes. The eyes are made
up of pixels with a color value of (255,0,0). We will target pixels with this color
and change them to blue.
To do this, we first determine the length of each row. Using the length, we
can point to any desired row in the pixmap as the pixel-row variable. Then we
loop through the columns of pixel values in this row.
If we determine the pixel value in any column has a red color, we change it
to blue. The code to do this is shown below:
Summary
In this chapter, we have seen how images are stored on the computer. Any
kind of graphics, whether a visually realistic simulation of the environment or a
simple 2D drawing, is eventually saved as a 2D raster image on the computer.
Storing and viewing these images is just the tip of the iceberg: we can use these
images in our own projects and even modify them to achieve desired results and
special effects. The techniques we have discussed here are used extensively in
production suites for image processing and gaming. They form the basis for the
more complicated image enhancements and effects used in the industry. In the
next chapter, we shall put together all our current working knowledge to design
and develop a 2D game.
Let The Games Begin
In the last few chapters, we learned the basics of 2D computer graphics. We saw
how to draw simple shapes using OpenGL and how to assign colors to them. We
learned how to make these shapes move about in our CG world. We also saw
how to save, load, and modify computer images. Let us make things interesting
by using all our knowledge to design a 2D game.
When it comes to games, we need real-time graphics-that is, the program has
to be interactive and have an immediate response to input. You press a button,
and the graphics changes. You drag the mouse, and a character moves in
response. In this chapter, we shall learn some techniques to interact with user
input in real time.
We begin by first exploring what a computer game really is and discussing
the traditional processes used in designing one. Our discussions on game design
will pave the way for a real game implementation.
In this chapter, you will learn the following:
w What a game is
w What steps production houses follow when producing a game
w How to follow this process to design and implement your own game
--- --- - -
A Story A Game
1. Audience
The first step in our game design process is to identify the target audience. For
the purposes of this exercise, let us pick children as our audience. In particular-
boys between the ages of 8 and 12.
Once the audience is chosen, research is carried out to identify the likes and
dislikes of this group. Say we find out that among the many things the boys love
to do, aiming and shooting at objects rank high.
Let us define our game to be a shooting game. The goal of the game will be
to provide the player with the opportunity to aim and fire at objects. The game
will also enhance the player's fine motor skills and hand-eye coordination.
2. Environment
Next, we must determine the environment in which the game will be played. We
have already defined the game to be a shooting game, but what characters are
playing in the game and what environment are they set in?
Let us define the game to be set in outer space. We need a character that will be
controlled by the player. This character will allow the player to shoot. We also
need a character to shoot down. We further want these characters to blend into
the environment selected.
With this in mind, we define a user-controlled spaceship as the shooter, and
meteors that appear randomly as the target. In order for the game to have a good
story, we probably need a third character which is the object that the user is
trying to protect. A silhouette of planet Earth would be a good choice, since
everyone is motivated to protect it from being hit by meteors!
3. Interactive screenplay
The screenplay, if appropriate, contains the dialogs and the storyline imple-
mented in the game.
For this game, let us define a simple screenplay. The game opens with a space
setting simulated with stars and sky. The tip of planet Earth is at the bottom of
the scene. A spaceship is hovering on top of the-earth. The player can move the
ship left and right and shoot bullets upwards. Meteors, generated by the game,
randomly appear in the sky. The player needs to shoot down these falling
meteors. If a bullet hits a meteor, it is destroyed. If the meteor hits Earth, then
the game ends with an explosion.
The interactive logic of this game can be shown as follows:
Meteors appear
\ J
Meteor shot down?
Meteor hits
and bullet
4. Storyboard
Storyboards aid in the design process by illustrating the game in motion. Usually,
storyboards are quick sketches of key moments during the lifetime of the game.
Any quirks or inconsistencies in the game show up at this step.
Storyboard illustrate only the main points of the story. For this game, the
following key points would be illustrated (the actual illustrations are left as an
exercise to the reader):
The opening scene: The spaceship hovering:
a Meteors appearing randomly in space
User positioning spaceship and firing at meteor
a End of game when meteor hits earth
5. Implementation
Finally, implementation issues detail the choices made to develop the game.
Some of the choices are as follows:
Is the game 2D or 3D?
We have no choice for the moment: it will be a 2D game.
What kind of look and feel does the game have?
The game is set up to be in space. We are going for a cartoonish look as opposed
to abstract or surrealistic.
How are the characters implemented?
The sky and stars can be implementcd using a background image. We can
probably get away with defining the earth to be part of this background image,
as long as we know its coordinate data in order to determine collisions.
The meteors are merely point shapes that randomly appear in the sky and fall
down towards Earth. Bullets are also point shapes that shoot upwards when the
user clicks the right mouse button.
What are the input rnethod~~for the user?
In arcade games, a joystick enables the user to control the game. A joystick
works very similarly to a mouse. It allows the user to move characters left, right,
up, and down, and allows the user to press a button to fire. For our game, the
mouse will provide thc input parameters. The user drags the mousc with the lcft
button down in order to move the spaceship. Clicking on the right mouse button
controls firing the bullets.
What are the tools needed to develop the game?
C++ and OpenGL We will develop the game in C++ since this language lends
itself very nicely to game development.
What platform will the game run on?
Windows (but you can compile and run it on any platform)
4.3 Implementing the Game
By now you are probably more than ready to roll up your sleeves and start
implementing the game. Let us begin. The algorithm for the game (based on the
interactive logic chart we drew up earlier) is as follows:
Let us begin developing the game by first defining the characters that play in our
game.
The Characters
Object oriented programming lends itself very nicely to game programming,
since all the functionality of the characters can be encapsulated within a class.
We shall use simple C++ classes to create the characters in the game. If you have
never used C++ before, do not despair! The code is very similar to C and you
will find it very easy to follow the logic of the code. Just think of a C++ class as
a C struct.
class Meteor
{
public:
Meteorlint ulD);
-
virtual MeteorO;
void DrawO; //Draw the meteor
void DrawCollisionO; //Draw the meteor that has collided
void UpdateO; //Update the meteor location, for this example simply
decrement it's y-coordinate in order t o move it down
boo1 HitEarthO; //returns true if the meteor has hit Earth
boo1 OutOfScreenO; //returns true if the meteor is out of screen
GLint ID; I/ unique ID t o identify the meteor
GLfloat* GetLocationO {return location;)
private:
GLfloat location[2]; I/ meteor's location. location[Ol=x-, location[ll = y- coordinate
1;
The location array stores the current (x,y)-coordinates of the meteor. For this
example, we let all meteors fall straight down along the y-axis.
The update hnction merely decrements the location[l] variable in order to
make the meteor fall down. You can experiment with giving meteors an x-
direction as well.
The Draw routine draws a white colored point @Vertex) at the meteor's
current location, whereas the DrawCollision routine draws a bigger red colored
point to indicate a collision.
The functions HitEarth and OutOfScreen test the meteor's current location to
determine if it has hit Earth or is out of the boundary of the defined world
coorindates. It returns a TRUE value if the test is positive.
We use the ID variable to uniquely identify each meteor.
The entirc code for this object can be found under the directory Example4-I,
in files Meteo~ h and Meteo~cpp.
The function createMeteor is used to randomly create meteors during the
game.The function generates a random numbcr betwcen 0 and 1 and only crcatcs
a meteor if this number is less than a constant. This is to ensure we do not have
too many mctcors bcing generated.
void createMeteor0 {
if I IIfloatlrandOlRAND~MM)< 0.991
return;
SYSTEMTIME systirne;
GetSystemTime(&systime);
-
int ID systime.wMinute*60+systirne.wSecond;
-
Meteors[lDl new MeteorllD); llcreate a meteor with identifier
1
Meteors are created only if a random number generated between 0 and 1 is
less than a constant value (0.99). Decreasing this constant will causc more
meteors to be created. Each meteor is instantiated with a unique ID that is an
integer value. Thc ID of the metcor is derived from the minute and second of thc
current time and is unique just as long as the player does not play the game for
more than an hour. This ID is used as a key to inscrt and locate the meteor in a
meteormap:
We use an STL containcr-a map in this case-for storing the meteors. STL
maps are an easy way to store objects in an array indexed by a key. In this case,
the ID is the kcy used to store and retrieve the corrcsponding mctcor objcct from
the MeteorMap. STL iterators are used to loop through STL containers. More
information on how to use STL can be found in GLAS95. You can easily changc
the code to manage your meteor container as a linked list.
class Bullet
{
public:
Bulletlint 1.110, int x, int yl; IIDefine a new bullet at location(x,y).
-
virtual BulletO;
void Draw(); //Draw Bullet
void UpdateO; //Update Bullet location
boo1 OutOfScreenO; //returns true if Bullet is out of screen
boo1 CollidelMeteor "I; //returns true if Bullet collides with Meteor
GLfloat ID;
private:
Bullets are created when the user clicks the right mouse button. We shall see
shortly, how to monitor for mouse events. The createBullet function is called
when the right mouse button is clicked. The function is defined as follows:
class Spaceship
{
public:
SpaceShipO;
-
virtual SpaceShipO;
void SetPielslBlTMAPlNFO "ssinfo, GLubyte "sspixels); /I set the pixels and bitmapinfo
for spaceship
void DrawO; //Draw the spaceship
void UpdatelGLfloat x, GLfloat yl; llupdate location of spaceship
GLfleat GetXLocationO{ return location[Ol;)
GLfloat GetYLocationO {return location[ll;)
void StartMoveO; llstart moving the spaceship
void StopMoveO; llstop moving the spaceship update of
spaceship will not do anything
private:
GLfloat location[21;
The function Setpixels is used to initialize the pixmap of the spaceship. The
Draw routine of the spaceship object draws a pixmap at the object's current
location.
The spaceship is in motion when the user clicks down the right mouse and
drags the mouse around. The moment the user releases the button, the spaceship
stops moving. The Boolean variabe b-updates keeps track of whether the
spaceship is in motion (and hence whether it's location needs be updated).
The Update function is called only when b-updates has a value of TRUE. It
takes as parameter the current Mouse location, and draws the spaceship at this
location. For this exercise, we move the spaceship only left and right, so only the
x-coordinate is required for motion.
User Input
OpenGL and the GLUT library provide a number of functions to create event-
handling threads. An event-handling thread is a separate thread of execution that
listens for events such as user input, timers etc. If an event is detected, a call is
made to the appropriate "callback" function. The callback function can then
handle the event as desired. We saw event handlers for the Display and Reshape
events in the previous chapters. Event-handling threads are the crux of all gaming
programs. The GLUT function used to create a mouse event-handling thread is
glutMouseFunc~voidl*funcl~int
button, int state, int x, int yll
glutMouseFunc sets the mouse callback for the current window to be the
function func. When a user presses and releases mouse buttons in the window,
each press and release generates a call to this function. The callback function
receives information about which button was clicked
(GLUT-RIGHT-BUTTON or GLUT-LEFT-BUTTON), the state of the button
(GLUT-UP or GLUT-DOWN), and the x,y location of the mouse click within
the window (in the world coordinate system). In our example, we define our
mouse handling callback function to be
When the right mouse button is clicked up, we want to fire a bullet. The
bullet starts off at the center of the spaceship. If the spaceship was being dragged
around, we also want to stop moving it. If the button is clicked down, we want
to start moving the spaceship.
is used to set the motion callback function. The motion callback for a window is
called when the mouse moves within the window while one or more mouse
buttons are pressed. We want the location of the spaceship to be updated when the
user drags the mouse around. We define our motion callback function as follows
Notice something odd? We don't use the same y-coordinate that we receive
from the mouse click! Our world coordinates set the origin to be at the bottom
leftmost corner of the window. Y is increasing as we go up the screen. However,
Windows uses (0,O) at the top left comer of the window soy- is increasing going
down! The location of mouse events is in the coordinate system of the window
and needs to be reversed to map correctly into our world! The code for this class
can be found under Example4-1, in files SpaceShip.cpp and SpaceShip.h.
Timer Callbacks
Similar to mouse event callbacks, GLUT has the ability to register timer
callbacks. Timer callbacks get triggered at regular intervals of time-the "ticks"
of the game. This mechanism is extremely useful in games, when you want the
game logic to be called at regular ticks.
This means that the function timer (listed below) will be called after 100
millseconds. The timer function creates meteors randomly, forces the Display
function to be called and finally makes another call to re-register the timer
callback, starting off the process all over again.
void initlvoidl{
/I Timer callback
glutTimerFunc(100, timer, 0);
/I Define mouse callback function
glutMouseFunclgetMousel;
/I Define Mouse Motion callback function
glutMotionFunclgetMouseMotionl;
All the classes and functions can be found under the directory, Example4-1. All
the images can be found under the directory, ImageslChapter4.
The program is defined in the simplest manner possible for easy readability.
It is not the most efficient code. There are a number of tricks you can do to speed
up the program: Redrawing using XOR (remember, care must be taken to make
sure color patterns work during XOR), minimizing the number of loops, etc.
You can also add many more bells and whistles to get cool effects, such as an
animation of the planet exploding instead of a display of a few static images, a
fancier meteor object, etc. We leave this part of the game as an exercise for the
reader.
Summary
In this chapter, we have combined all of our working knowledge in Computer
Graphics to develop a computer game. We have learned the fundamentals of
game design and the processes involved in designing and developing a game. In
chapter 11, we will have the opportunity to design a 3D game.
Section 2
It's 3 0 Time!
We hope you have enjoyed your ride through the 2D world. With a solid
understanding of the 2D math involved in this world, we are ready to venture
into the world of 3D graphics.
The mathematics in a 3D world is an elegant extension of its 2D counterparts.
Most concepts in 3D are developed from 2D by simply adding a third (z-) axis.
Positions of objects are now described as (x,y,z) triplets instead ofjust (x,y) pairs.
Transformation equations are defined against three axes, not two.
But 3D graphics is a lot more than just drawing simple objects. This section
begins our quest into visual realism: how can we simulate a 3D world in the
computer and then compute images that render out like photographs?
3D Productions are built up on three basic building blocks: modeling,
rendering and animation. The first two blocks-modeling and rendering-form
the basis for this section. Together they determine the final displayed image.
In Chapters 5 and 6, we will study the basics of modeling and rendering, and
in Chapter 7, we will introduce you to some of the more advanced concepts in
the field. Chapter 8 is entirely devoted to Maya, the most popular 3D tool used
in the graphics industry.
By the end of the section, you will be able to define a 3D world and render
it on the computer to produce stunning imagery. So hold your breath and get
ready for the 3D roller coaster.
Chapter 5 -- -
3 0 Modeling
The first and most critical pillar of 3D graphics is modeling. Modeling is the
process of creating a 3D model in the computer.
In lay terms, a model is a representation of a concrete or abstract entity. This
representation can be of various kinds. Quantitative models use equations to
represent and describe system behavior; organizational models use hierarchies to
represent classification schemes. A CG model refers to the geometrical
representation of the entity. The purpose of the model is to allow people to
visualize the structure of the entity being modeled.
When we model an object on the computer, we tell the computer about the
shape, spatial layout, and connectivity of components that compose the object.
In the last section, we saw examples of how to create simple 2D models such as
polygons and circles. In this chapter, we expand our horizons to a 3D world. We
will learn the process of viewing three-dimensional worlds and how to create,
compose, and transform models in this world.
In this chapter, you will learn the following concepts
w Representation of the 3D system
3D math: vectors, matrices and transformations
Creating models of
primitive shapes
generic shapes
w Viewing the 3D world
w Creating hierarchical models using transformations
We begin our discussions by first extending our knowledge of the 2D system to 3D.
5.7 The 3 0 System
We can relate to a three-dimensional space because we see our own world in 3D.
Not only do objects in our world have length and height, as they do in the 2D
space, but they also have depth associated with them. They can also be located
closer or farther away from us.
In order to represent a 3D world, we need to extend our coordinate system into
three dimensions. Every point in the 3D world is located using three coordinate
values instead of 2. In order to define points with three coordinates, we define a third
axis, typically called the z-axis. The z-axis is perpendicular to the x- and y- axis (i.e.,
it is at 90 degrees with respect to the two axes - also referred to as orthogonal). All
three axes meet at the origin defmed as (0,0,0) as shown in Fig.5.1. This is our 3D
world coordinate system. In this book, we follow a right-handed coordinate system,
which means that the positive z-axis points out of thescreen and towards us.
All points and vector representations that we learned about in chapter 2 can be
extended to 3d by adding a third component, z. Points in 3D space are identified by
a triplet of (x,y,z) values. The vector notation for this triplet is represented as:
In Chapter 1, we saw how objects are defined by the key (2D) vertices
El
defining its shape. 3D vertices define the shape of on object in 3 Dimensions.
The OpenGl function to define a vertex point (x,y,z) in 3D is
Using this function, we can modify our circle-drawing function from Chapter
1, to the 3D world. In the following code, we define the points of the circle in
the x-z plane (not the x-y plane).
Fig. 5.2: Circle bn the XZ plane
void MyCircle3flGLfloat centerx, GLfloat centery, GLfloat centen, GLfloat radius){
GLint i;
GLdouble theta;
glBeginlGL-POINTS);
--
for li 0;i < circlegoints; i+ + l {
theta 2*PI*ilcirclegoints;
glVertex3flcenterx+ radius*cosfthetal, centery, centen+ radius*sinltheta));
1
glEndll;
1
The circle is centered at the point: (centerx, centery, centerz). The function
@Begin function is used to define the primitive being drawn. In this case, we
merely draw points along the circumference of our circle. How would you
connect the points to draw a connected circle?
3 0 Math: Vectors
A vector in 3D has the same semantics as in 2D - it can be thought of as a
displacement from one point to another. Consider two points P(1,- 1,l) and Q(3,-
2,2) as shown in Fig.5.3. The displacement from P to Q is the vector
v= (2,-1,I) = 2
[-:I
calculated by subtracting the coordinates of the points individually. This means
that to get from point P to Q, we shift right along the x-axis by 2 units, down the
y-axis by one unit and outward by one unit along the z axis.
Any point PI with coordinates (x,y,z) corresponds to the vector Vp 1 , with its
head at (x,y,z) and tail at (0,O) as shown in. That is, a 3D point is essentially a
vector with its tail at the origin (0,0,0). A unit vector is a vector that has a
magnitude of one unit. For example, the unit vector along the z-axis is:
r:iL !A
In Example 5.1, we draw unit vectors along the positive x-,y-,z-axes in red
Fig. 5.3: vectors in 3D Space
green and blue colors respectively. The code to draw the axes is as follows:
IX Axis
glBegin(GL-LINES);
glColor3f ~1.0,0.0,0.0);
glVertex3RO,O.,O.);
glVertex3f(l,O,.O);
glEnd0;
IN axis
glBeginlGL-LINES);
glColor3f 10.0, 1.or 0.0);
gNertex3f10.,0.,0.1;
gNertex3f(O.,I ,Ol;
glEndl1;
lR axis
glBegin(GL-LINES);
glColor3f (0.0,0.0,1.0);
glVertex3f(O.,O.,O.);
glVertex3f(O.,O.,l .I;
glEnd0;
We then call the circle-drawing routine to draw two circles with unit radii,
one centered (0,0.5,0) and the other at (0,0,0):
The circle-drawing routine has additional code to vary the colors of the point
based on its location in space. The output displays the three unit axes and the two
circles. The entire code can be found in Exarnple5-I/Example5_1.cpp.This example
z
Fig. 5.4: Two circles in 3D Shape
contains lines of code that you do not understand as of yet - specifically with
regards to setting up the 3D world. Do not worry; by the end of this chapter, it
will all start to make sense.
21 +z2
As shown in Fig.5.5, the semantics of the addition remains the same as in 2D.
Subtraction is similarly extended.
If u = u2 v =
u3
Then the resultant cross product of the two vectors is
ulv2 - u2vl
This resultant vector is the normal vector to the plane formed by U and V as
shown in Fig.5.6.We shall use the concept of normal vectors heavily when we
learn about lighting in Chapter 6. The code for calculating the cross product of
two vectors and normalizing the result (creating a unit vector) can be found in
under the installed directory for our source code in the file: uti1s.h. The code is
shown below. We assume the vector values are stored in an array of 3 elements.
which is the (unit) normal vector to the plane defined. (Remember, this vector
just specifies the direction of interest - can you guess what the vector is?). We
draw the three coplanar points, and place a fourth point it's normal vector:
The function expects a pointer to an array of three floating point values, as shown
in the sample code above. We then draw line primitives connecting all the points:
The result displayed forms a triangular pyramid as shown in Fig.5.7.Yes -this is our very fitst
3D model. That was hqwasn't it? Let us look deeper into the process of creating 3D models.
5.2 3 0 Modeling
CG scenes depict objects of all different kinds: people, trees, flowers, fire, etc.
Each one has radically diffcrcnt characteristics in terms of look and feel. So it
should come as no surprise that there is no one standard modeling
methodology-each object begs a different treatment to do full justice to the
model.
Thc most popular and commonly used method to model objects is to use
small polygons to approximate the surface. This method is popular due to ease
of use, speed of display, and the abundance of algorithms to deal efficiently with
polygon bascd models. Let us look into more detail on polygons and how thcy
are used to model more complex shapes. In Chapter 7, we shall see some
advanced techniques of representing models.
The Polygon
A polygon is much like a cutout piece of cardboard. Drawing a polygon is like
playing a game of connect the dots: each dot is a vertex defining the polygon.
You need at least three vertices to define the polygon. Each line of the polygon
is called an edge.
More formally, a polygon can be defined as a set of non-crossing straight
lines joining coplanar points to enclose a single convex area. Typically, most
graphics packages (including openGL) support single convex area polygons. A
single area means that the enclosed area should not bc divided. The convcx
requirement means that given any two points within the polygon, you must be
able to draw a straight line connecting these two points without going outside thc
area of the polygon, as shown in Fig.5.8.
The polygons arc required to be flat (that is, to be defined in a single plane).
(a) no area-illegal (b) single convex area (c) two areas, illegal
(d) single convex area (e) single, but non-convex area - illegal
void myPyramidO{
11 front face, vertices defined in counterclockwiie order
glColor3f (1.0, 0.0, 0.0);
glBeginlGL-POLYGON);
gWertex3fv(PII;
gRlertex3fv(wI;
glVertex3fvlP3I;
glEnd0;
I1 left face, vertices defined so that when this face is facing the camera, the points are in
counter-clockwise order
glColor3f 10.0, 1.Or 0.01;
glBegin(GL-POLYGON);
gNertex3fv(P1I;
gRlertex3fv(P2I;
gRlertex3fvluvI;
glEnd0;
//right face
glColor3f 10.0, 0.0, 1.0);
glBegin(GL-POLYGON);
gNertex3fvlP3);
gNertex3fvlwl;
gNertex3fvlPZl;
glEnd0;
//bottom face
glColor3f (1.Or 0.0,I.O);
glBegin(GL-POLYGON];
glVertex3fvIP31;
glVertex3fvlP2);
glVertex3fvlPlI;
glEnd(1;
1
This function defines four polygons grouped together to form the shape of a
pyramid. The polygons are colored differently for sake of identification. By
default, OpenGL draws polygons as filled (with the current color).
The openGL function
can be used to define the treatment for each face of the polygons drawn.
We change the drawing mode for polygons so that we see the front faces of
our pyramid as filled, and the back faces as lines.:
Back-face Culling
In real life, you cannot see all sides of a solid object. You see only the sides facing
you! The sides facing away from you are obstructed from view. To achieve this
effect in CG, we make use of the front- and back-facing polygon concept. (And
you were wondering why it was such a big deal!) The front- facing polygons are
facing towards the camera and drawn into the scene. The back-facing polygons
are facing away from the camera and hence are culled from the scene. This
process is known as back-face culling.
In Fig.5.10, we show our pyramid and a sphere with all the sides visible, and
5. 3D MODEIING
CHAPTER
You will need to enable face culling with glEnable(). If we modify the init
function in Example5-3 to be defined as
void init(void){
llset the clear color to be black
glClearColor~O.O,O.O,O.OIO.O~;
glEnable(GL-CULL-FACE);
glCullFacelGL BACK);
g l ~ o l y g o n ~ o d eFRONT,
( ~ ~ GL LINE);
~ l ~ l ~ o l ~ ~ o n G~LINEI; ~ o d e l ~ ~ - ~ ~ ~ ~ ~
...
1
When you run the program, you will see only the pyramid's front facing
polygon. The back facing polygons are culled out--exactly what we wanted. The
reader is encouraged to try rotating the pyramid to see how back-face culling
affects the final output. By definition, as the pyramid turns around, the faces'that
were back-facing become front-facing (and vice versa).
Normal vectors can also be used to determine the orientation of polygons.
There is a one-to-one correspondence between the direction of the normal vector
and the order in which the polygon vertices are defined. If the normal vector of
the polygon points in the direction of the camera, then the polygon is front
Fig. 5.11: Normals define the face of a polygon
facing, else it is back facing, as shown in Fig.5.11. We shall look into normal
vectors in more detail in the next chapter.
Polygon mesh
Polygons have limited usefulness by themselves. The real power of polygons
comes when they are used together in a mesh structure to make a more complex
shape. A polygon mesh (referred to as strips in openGL) is a collection of polygons
such that each edge is shared by at most two polygons, as shown in Fig.5.12.
-
Fig.5.12: Polygon mesh approximating a circle
Cube
The cube model (or box) is one of the most common shapes occurring in nature.
If we know the center about which the cube is based, then the length, height and
depth completely define the cube as shown.
z
Fig.5.13: A cube defined by its three dimensions
In Example 5-4, we draw a cube, centered at the origin, as follows:
11base
glBeginlGL-POLYGON);
glVertex3fl-lengthl2, -heightl2, depthl2);
glVertex3fl-lengthl2, -heightl2, -depthl2);
glVertex3fllengthl2, -heightl2, -depthl2);
gNertex3fllengthl2, -heightl2, depthl2);
glEnd0;
11front face
glBeginlGL-POLYGON);
glVertex3fl-lengthl2, -heightl2, depth/ZJ;
glVertex3fllengthl2, -height/2, depthl2);
glVertex3fllengthl2, heightl2, depthl2);
glVertex3fl-lengthl2, heightl2 depthl2);
glEnd0;
11right side face
glBeginIGL-POLYGON);
glVertex3fllengthl2, -heightl2, depthl2);
glVertex3f(lengthl2, -heightl2, -depth/2);
glVertex3fllengthl2, heightl2 -depthl2);
glVertex3fllengthl2, heightl2, depthl2);
glEnd0;
11back side face
glBegin(GL-POLYGON);
glVertex3f(-lengthl2, -heightl2, -depthl21;
glVertex3fl-lengthl2, heightl2, -depthl2);
glVertex3f(lengthl2, heightl2, -depthl2);
glVertex3fllengthl2, -heightl2, -depthl21;
glEnd0;
I1left side face
glBeginiGL-POLYGON);
glVertex3fl-lengthl2, -height/2, depthl2);
glVertex3fl-lengthl2, heightl2, depthl2);
glVertex3fl-lengthl2, heightl2, -depthl2);
glVertex3f(-lengthI2, -height/Z, -deptN2);
glEnd0;
11top
glBeginlGL-POLYGON);
glVertex3f(-lengthl2, heightl2, depthl21;
glVertex3fllengtN2, heightl2, depthl21;
glVertex3f(lengthl2, heightl2, -depthl21;
glVertex3f(-lengthl2, heightl2, -depthl2);
glEnd0;
also generates a cube using polygons. It accepts the length of the cube as input
and draws a cube with equal sides, centered at the origin. The Display routine in
Example5_4/Exarnple 5-4.cpp makes calls to both cube drawing routines,
translating both of them for easier viewing:
void Display(void1
Sphere
Spheres are the most symmetrical shape occuring in nature. For this reason, they
are also the simplest to define. To completely define a sphere, we need to define
its radius, R, and the location of its center, 0. If we center the sphere around our
world origin, then any point P on the surface of the sphere can be defined by the
angles 8 and (I that it subtends at the origin as shown in Fig.5.14.
P=(Rcosecos$, Rsine, Rcosesin$)
-
GLdouble inc P1112;
GLdouble theta, phi;
-
boo1 even true;
-
for (phi 0; phi < 2*Pl;phi+ -incl {
-
for Itheta --Pl12; thetaclP112-inc1;theta + inc){
glBegin(GL-POLYGON);
glVertex3f(radius*cos(thetal*cos(phi), radius*sinltheta),radius*cos(theta)"sin(phill;
glVertex3f(radius*cos(theta+ inc)*coslphi), radius*sin(theta + inc),radius*cosltheta + inc)*sin(phill;
glVertex3flradius*cos(theta + inc)*cos(phi+ incl,
radius*sin(theta+ inc),radius*cosltheta + incl*sin(phi + incl);
glVertex3flradius*cosltheta)*cos(phi+ incl, radius*sinlthetal,radius*cosltheta)*sinlphi + inc));
glEnd0;
The inc variable defines how many polygon slices and stacks we define for
the mesh defining the sphere. You can also use the GLUT built-in function:
which accepts the radius and the number of slices and stacks desired as arguments.
In Example5-5/Example5-5.cpp, we use both calls to generate spheres. Try
turning back-face culling on in this example by making calls to the functions
You will not see the back polygons in the sphere that we defined Yes, we did take care to
make sure polygons were definedwith the correct orientation.In the case of objects with a large
number of polygons, back-itice culling makes a big difference in the clarity of the image.
Cone
A cone can be thought of as having a circular base and a conical surface. The
Fig.5.15: Approximating a cone
base of the cone can be defined as a circle on the x-z plane: the math is similar
to the way we constructed a circle in Chapter 1. We approximate the conical
surface of the cone by drawing a set of lines connecting the tip of the cone (a
point along the y-axis) to the vertices along the circular base. The following code
shows how to draw a cone (upper conic only) in a 3D world using openGL. The
cone is assumed to be centered about the origin.
Model files
What is stored in a model file? If the model is a primitive, then the key
parameters defining the primitive are saved. If it is a generic model, then
information about the (polygonal or cubic) primitives used to construct it are
saved.
There are many formats in which the model information can be stored. Maya
stores models in its own propriety format. For this book, we shall use an open
format called the VRML format. You can find many VRML models available for
free over the Internet. Indeed, we encourage you to go ahead and download
VRML model files yourself to experiment with. VRML files store polygonal
information as follows:
For each model in the file:
First, all the coordinates of the vertex points are listed as (x,xz) triplets. Then the
index of each vertex in this list is used to spec@ the vertices forming the polygonal
faces of the model, with a -1 as the delimiter. For example, to draw two triangles
that form the faces of a unit square (Fig.5.17), the vertices would be listed as
0. 0.0. I1vertex 0
1.0. O., I1vertex 1
1. 1. O., //vertex2
0. 1.0. Ilvertex 3
Normal vectors to each polygonlvertex are defined in a similar manner. First the
normal vectors are defined, and then the indices are defined determining the face
to which the normal applies. Of course, we can actually calculate the normal
vectors for each polygon, by computing the cross product of its defining vectors!
VRML models also contain material information, which we shall not look into.
A version of code to read in a very simple VRML file can be found under the
directory where you installed our software, in the files: vrml.cpp and vrm1.h. In
these files, we define a function
#define MAXSHAPES 25
#define MAXCOORDS 3000;
This structure will hold the coordinate data for up to 3000 vertices (900013)per
shape and upto 25 shapes. The normal data is similarly defined, although we will
not use it in this chapter. The index array holds information for up to 3000
triangular faces and up to 25 shapes.
GLint indices[MAXSHAPES1[3*MAXCOORDSl;
For each shape, both arrays hold the three coordinates/indices of each
vertedpolygon in a linear one-dimensional manner. We maintain a count of the
number of polygons defined by each shape
GLint noofpoly[251;
-
noofshapes ReadVRMLI'..\\Models\\robot.wrl", &coords[OJ[O], &normals[Ol[Ol,
&indices[Ol[Ol,&nindices[Ol[OII
&(noofpoly[Ol), 25,3000);
We can draw the models in many different ways. The simplest way is to draw the
triangular faces one by one using the glVertex command.
for (j-O;j<noofshapes;j+ + 1 {
-
for (i O;i<noofpoly[j1"3;i -i +3) {
glBeginlGL-TRIANGLES);
glVertex3f~coords[jl[3*indices~[ill,
coords[jl[3*indices[jl[il+ 11,
coords[jl[3*indices[jl[il+ 21);
glVertex3flcoords[jl[3*indices[jl[i + 1I],
coords[jl[3*indices[jl[i + 1I+ 11,
coords[jl[3*indices[jl[i + 1I+ 211;
glVertex3f(coords[j1[3*indices[jl[i+ 211,
coords[jl[3*indices[jl[i + 21+ 1I,
coords[jl[3*indices[jl[i + 21+ 211;
glEnd0;
11
An easier way to define the vertices is by making a call to the OpenGl function
-
for (j O;j<noofshapes;j + +1 {
5.5 3 0 Transformations
We have seen how to create models in the 3D world. We discussed transforms in
Chapter 2, and we have used some transformations to view our 3D models
effectively. Let us look into 3D transformations in more detail.
Just as 2D transformations can be represented by 3 x 3 matrices using
homogenous coordinates, 3D transformations can be represented by 4 x 4
matrices. 3D homogenous coordinates are represented as a quadruplet (x,y,z, W).
We discuss the representation of the three most commonly used transformations:
translation, scaling, and rotation. 1 O O T x
The translation matrix T(Tx, lj,Tz) is defined as T =
void glLoadldenti(void1
glLoadMatrix4f(GLfloat *m)
glMukMatrix4f(GMoat "m)
m9 m10 m l l m12
m13 m14 m15 m16 1
Additionally, OpenGL provides three utility functions for the most
commonly used transformations.
5.6 Viewing in 3 0
The solution to transforming a 3D world into a 2D plane is a little more complex
and is accomplished by the use of projections. Projections provide the transforms
needed to map 3D points onto a 2D plane, as shown in Fig.5.19. To determine
the projection, we need to define a view volume of the world (the world
coordinates), a projection that defines the mapping of the view volume onto a
projection plane, and the viewport for final display. Conceptually, objects are
first clipped against the 3d view volume, projected, and then finally mapped into
the specified viewport.
Whew! That was a mouthful. Let us see if we can understand this process by
way of a real-world analogy. The essential goal in 3D CG is to give the viewer
the impression that he is looking at a photograph of a three-dimensional scene,
much the same way we photograph our 3D world onto a 2D film. 3DCG
simulates the process of the real-world camera to create 3d images. Let us first
understand how a real camera records an image of our real live 3D world by
using the example of a simple pinhole camera. We will then extend this
discussion to see how CG simulates this process.
Pinhole Camera
Fig.5.20 shows a pinhole camera. The camera is a simple box with photographic
film F at the back and a small hole H that allows light in when opened.
Consider what happens when we take a photograph of object X. We open
hole H for a fraction of a second. A ray of light hits X at point P, passes through
H and hits the film at P', causing the film to be exposed at this point. The point
P' forms the image of point P on the negative. All points on the object are
mapped onto the film in this fashion. Another object Y, of the same size but
ObjectX
The CG Camera
The steps needed to generate the CG image can be defined analogously to the
steps we would take a photograph in the real world. We need to define the
following:
1. Viewing Transformation
The viewing transformation sets the viewing position and is
analogous to positioning and aiming a camera.
2. ObjectIModeling Transformation
The modeling transformations are used to position and orient the
model within the world. This process is akin to arranging the scene to
be photographed into the desired composition. For example, you can
rotate, translate, or scale the model-or perform some combination of
these operations. We saw how to do this in a 2D world. In 3D, the
concept is enhanced to include a third dimension defined by the z-axis.
viewing volume
Fig.5.23: Projection Transformation
4. Viewport transformation
The viewport transformation determines the shape of the available
screen area into which the scene is mapped and finally displayed.
This process is analogous to determining how large the final
photograph will be. This step is same as in the 2D world - when
we finally map the world coordinates onto the display.
ObjectIModel Transformations
We can move, resize, or orient the models in our 3D world differently to create
a composition or scene. We saw in Chapter 2, that these transformations are
called object transformations or model transformations. There is an alternative
way to think about model transformations: moving the camera in one direction
will have the same effect as moving all the models in the scene in the opposite
direction! For this reason, viewing and modeling transformations are
inextricably related in OpenGL and are in fact combined into a single modelview
matrix. You should find a natural approach for your particular application that
makes it easier to visualize the necessary transformations, and then write the
corresponding code to specify the matrix manipulations.
You must call glMatrixMode0 with GL-MODELVIEW as its argument prior
to performing modeling or viewing transformations. In Example 5-9, we modify
the Modelview matrix to move our pyramid model back in z- by 4 units, scale it
up and constantly keep rotating it around the y-axis.
glloadldentity 0;
11 translate pyramid back by 4 units, and down by 1 unit
gITranslatef(O.,-I .,-4);
/I scale pyramid
glScalef(2.,2.,1 .I;
Which transform is being applied first? What will happen if we change the order
of the transforms? The entire code can be found in Example59/Example5-9.cpp.
Viewing Transformation
A viewing transformation changes the position and orientation of the viewpoint
itself. Essentially, the viewing transformation repositions the camera and the
direction it is looking. As discussed earlier, to achieve a certain scene
composition in the final image or photograph, you can either move the camera
or move all the objects in the opposite direction. In Example.5-9, the translation
routine can be thought of as actually moving the camera position by 4 units
towards the object, instead of the object being moved back. Readers are
encouraged to try experimenting with this approach of transforming the scene.
Remember that transformations are applied in the reverse order of the
commands specified. Viewing transformation commands must be called before
any modeling transformations are performed. This will ensure that the viewing
transformations are applied to all the models after the model transforms.
You can manufacture a viewing transformation in OpenGL using different
techniques. One is using the standard glTranslate and glRotate functions. A more
popular method is to use the Utility Library routine gluLookAt() to define a line
of sight.
void gluLookAt(GLdoubleeyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery,
GLdouble centen, GLdouble upx, GLdouble upy, GLdouble upz);
This call defines a viewing matrix such that the desired viewpoint is located at
(eyex, eyey, eyez). The centerx, centery, and centerz arguments specifies a point
in the scene that is being looked at. The upx, upy, and upz arguments indicate
which direction is up (that is, the direction from the bottom to the top of the
viewing volume). This matrix is then multiplied to the current modelview matrix.
In the default position, the camera is at the origin, is looking down the negative
z-axis, and has the positive y-axis as straight up. This is the same as calling
The following code from ExampleS-I O/Example.5-I 0.cpp moves the viewpoint
closer to and farther from the pyramid, as the pyramid continues its rotation
about the y-axis. Notice that we call the viewing transform before calling the
object transforms. The matrix stack is specified in the init() fbnction.
- -
GLfloat zdist 3., zmove I;
void Displaylvoidl
{
glutSwapBuffers0;
glClear (GL-COLOR-BUFFER-BIT);
glLoadldenti I);
Projection Transformations
In general, projections transform points in an n-dimension coordinate system to
a coordinate system with fewer than n-dimensions. In 3DCG we are interested in
the projection from a 3D space to a 2D space--just like a shadow is a projection
of a 3D object (you) onto a 2D plane (the ground).
The projection transformation is defined by a viewing volume (the world
coordinates), the camera position or the viewpoint, and the image plane, also
referred to as the projection plane. The class of projections we deal with here is
known as planar geometric projections because the projection is onto a plane
rather than some curved surface. The Omnimax film format, for example, uses a
non-planar projection plane.
The viewing volume and the viewpoint determine how the 3D object is
projected onto the plane. We use straight lines, called projectors. Projectors are
drawn from the center of projection (the viewpoint) to each vertex point on the
object. The points of intersection between the projectors and the image plane
forms the projected image. The two basic classes of planar projections are
perspective and parallel, as shown in Fig.5.27.
Center of Center of projectibn .."
projection at infinity
Fig.5.27: Perspective and Parallel Projection
Perspective Projection
The most unmistakable characteristic of perspective projection is perspective
foreshortening: the farther an object is from the camera, the smaller it appears in
the final image. This effect occurs because the viewing volume for a perspective
projection is a frustum of a pyramid (a truncated pyramid whose top has been cut
off by a plane parallel to its base). Objects that are closer to the viewpoint appear
larger because they occupy a proportionally larger amount of the viewing volume
than those that are farther away. This method of projection is commonly used for
animation, visual simulation, and any other applications that strive for some degree
of realism because the process is similar to how our eye (or a camera) works. The
following command defines the frustrum of the viewing volume:
void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble
near, GLdouble far);
4
far b
glMatrixMode IGL-PROJECTIONI;
glLoadldentity0;
glFrustum (-1.O*lens, I .O*lens, -1.O*lens, I .O*lens, 1., 20);
11 gluPerspectiie(fang, aspect, 1., 20.01;
glMatrixMode (GL-MODELVIEW);
glLoadldentity(1;
gluL00kAt(0.,0.,3, O,O,-100,0.,1.,0.1;
11 rotate the pyramid
glRotatef(ang,O.,l.,O.);
-
if (ang > 360) ang
I1 draw the pyramid
- 0; else ang + + ;
myPyramidll;
glFlush(l;
glutPostRedisplay0;
If you change the near and far clipping planes instead, you will see the
pyramid gradually being clipped out of the scene. The glFrustum command is
not always intuitive. An easier command to define a perspective projection is
fovy is the angle of the field of view in the x-z plane (shown in Fig.5.28) This
value must be in the range [0.0,180.0].
aspect is the aspect ratio of the frustum, that is its width divided by its height.
Example5-11 can be modified to replace the command glFrustrum with the
command:
where the field of view (fang) of the camera changes from 30 degrees to 160
degrees to achieve the same effect as seen by glFrustrum. In general, we always use
the gluperspective function to define the perspective projection in our programs.
side view
/ front view
Fig.5.29: Orthographic projection: viewing volume
The output of a parallel projection is not realistic because objects retain their
size no matter where they are located. It is very usefbl for calculating
measurements and sizes of objects and is often used when modeling objects, in
engineering drawing, and in blue prints of architectural plans.
Parallel projections are called orthographic if the direction of projection is
orthogonal (at 90 degrees) to the projection plane. The most common type of
ortho projections are along the x-, y- and z- axis (called the front, top and side
views) where the projection planes are on they-z, x-z and x-y planes respectively
as shown in Fig.5.29. These projections are used extensively in modeling tools
to enable the process of 3d modeling.
With an orthographic projection, the viewing volume (the world coordinates)
is a box, as shown in Fig.5.30
near far
bottom
Fig.5.30: Orthographic viewing volume
Unlike perspective projection, the size of the viewing volume doesn't change
from one end to the other, so distance from the camera doesn't affect how large
an object appears to be. The direction of the viewpoint defines the direction of
5 3 0 MODEIING
CHAPTER 117
projection, but its location is ignored. The openGL command to specify the view
volume for orthographic projections is defined as
This code will display the front view of the pyramid. To see the top view, we
need to define the direction of projection to be along the y-axis. This is achieved
by rotating the viewing position 90 degrees about the x-axis by inserting the
following lines of code after the gluLookat command:
Similarly, rotate the viewing position by 90 degrees along the y-axis to get a side
view. The different-colored faced of the pyramid will provide you with a good
indication as to where you are looking.
Fig.5.31: A Snowman
Consider building the model of a snowman. We shall refer to him as Snowy,
as shown in Fig.5.31
Let us first consider the component objects that build up the snowman. The
base, tummy, and head are of course all spheres. The hands could be modeled
using cones or cylinders and the eyes using disks. The carrot shaped nose is just
a simple cone.
How would we define the hierarchy of such a model? We definitely want the
eyes and the nose to ride along with the face. Therefore we would define them to be
children of the head object. Similarly, the hands can be grouped as children of the
tummy. Now the base, tummy and head need to be parented in a way that would
enable us to move them as a single unit. In order to do this, we define what we call
a Null Node. A null node is used solely for the purpose of grouping together
components in a hierarchy. It has no graphical display in our 3D world. It does have
a location, which defines the pivot for any rotation and scale transforms applied to
it (and hence to its children). The introduction of the null node would lead us to a
hierarchical structure of the snowman as shown in Fig.5.32. Let us see how we can
use matrix stacks to define the hierarchical model of the snowman using OpenGL.
Matrix Stacks
The concept of a stack of matrices is used for constructing hierarchical models.
Matrices can be pushed on top of the stack and popped out by using the Push and
Pop matrix operators. At any given time, only the topmost matrix is used to
define the current transformation state. The stack being used is determined by
glMatrixMode0 - in this case we will just be using Modelview matrix stack.
OpenGl provides two commands to deal with stacks of matrixes.
The OpenGL command
void glPushMatrixlvoid1;
pushes all matrices in the current stack down one level. The topmost matrix (Mc
in the Fig.5.33) is copied, so its contents are duplicated in both the top and
second-from-the-top matrix. This is an effective way to save the current
transformation state while applying further transformations at the top.
Fig.5.33: Push operator
The openGL command,
void giPopMatrix(voidl;
pops the top matrix off the stack, destroying the contents of the popped matrix.
What was the second-from-the-top matrix becomes the top matrix. From the
previous fig., we effectively pop back to the saved transformation state: matrix
Mc. If the stack contains a single matrix, calling glPopMatrix() generates an
error. The Push and Pop commands are very usefbl to 'remember the last
transformation and to pop back to it when needed
Romove
additional top most
transforms matrix
Pop back 1-
to the -4 Mc 1
previously
saved
state
IlXform of eyes
-
GLfloat headXforms[91 {0.,0.,0.,0.,0.,0.,1 .,I.,I.);
--
GLfloat IEyeXforms[9] {0.,0.,0.,0.,0.,0.,1 .,I.,I.);
GLfloat rEyeXforms[9] {0.,0.,0.,0.,0.,0.,1.,1.,1.);
IlXform of nose
-
GLfloat noseXforms[9] {0.,0.,0.,0.,0.,0.,1.,1.,1.);
IlXform of hands
--
GLfloat IHandXforms[S] {0.,0.,0.,0.,0.,0.,1.,1.,1.);
GLfloat rHandXforms[91 {0.,0.,0.,0.,0.,0.,1.~1.,1.);
The drawing routine for each component accepts its corresponding xform array.
The drawing routine first saves the current transformation state by using a
PushMatrix comand. It then applies its local transforms, and then pops the stack
back to the original transformation state. For example, the bottom of the
snowman, with a radius of 1.5, units can be defined as follows:
to be defined just before the actual drawing code, then the local origin would be
located at the base of the sphere when the transforms are applied. The pivot point
for the bottom would hence be at its base.
This technique is often used to modify pivot points for components within a
model. In this case, we actually wanted to re-position the sphere 1.5 units up as
well. If we do not wish for the actual model to be re-positioned, we would then
apply a reverse translation to move the model back to its original location. This
process would modify the pivot point without affecting the position of the model.
We saw this process at work in Chapter 2, Example2.4.
All children of a component are drawn within the transformation state of the
parent. This ensures that the child is transformed along with its parent. Due to
the Pop operation, any transformations applied to the child are not applied back
to the parent. For example, the hands, which are children of the tummy, are
defined within the transformation stack of the tummy. The transforms of the
hands are applied only in the local routine defining the hand.
{
glutSolidSphere(l,20,201;
I1 draw children within parent's transformation state
The tummy has a radius of 1 unit and is transformed up by 3.9 units, so it
rests on top of the bottom (!). Notice that we apply the scale and rotate transform
before the translations. Doing so ensures that the pivot point for these operations
is at the local origin-the center of the sphere.
The left hand (and similarly the right hand) are defined as cones. By default,
they are rotated by 90 degrees and translated so that they are sticking out from
the surface of the tummy. The hands can be moved by modifying the
1HandsXforms array. Additionally, they will also be transformed when the
tummy is! The pivot point for the scale and rotate transforms for the hand is at
the local origin of the cone which is located at the center of it's base.
The head and its children (eyes and nose) are similarly defined. The final
snowman drawing code is as follows:
Note that the snowman object itself has no drawing component to it. Since there
are no transforms before the scale and rotate calls, the pivot point is at the local
origin: which happens to be the base of the snowman. This pivot is used for
transforms applied to the entire snowman hierarchy (snowmanXforms).
In Example.5-13, we rock the snowman back and forth about its base by
rotating it along the z-axis. At the same time, we also swing its hands up and
down and nod its head left to right (rotation along the y-axis)
void Display(void1
{
GLfloat ypos, xpos;
---
snomanXfoms[5] + 1"sign;
IHandXforms[S] + 2*sign;
rHandXforms[5] + -2"sign;
- --
headXforms[4] + 2"sign;
if (snomanXforms[51 301
-
sign -1;
else if IsnomanXforms[5]
-
sign 1;
-- -301
glClear(GL-COLOR-BUFFER-BIT I GL-DEPTH-BUFFER-BIT);
draw~SnowManIsnomanXforms, botXforms, tumXforms, headxforms, IEyeXforms,
IEyeXforms, noseXforms, IHandXforms, rHandXforrns1;
Try playing around with the various transformations and verify that the
hierarchy works as intended. The entire code can be found under Example.5-13,
in files: Example5-13.cpp, Snowman.cpp and Snowman.h. Make sure you
completely understand the concept of pivot points and how they can be modified
before moving on to the next section. Now, consider a slightly more complex
example for modeling a hierarchical model, that of the android.
Right Calf
#define ROOT 20
Wefine CHEST 19
#define NECK 18
#define FACE 17
#define LEFTUPPERARM 16
#define LEFTLOWERARM 15
#define LPALM 14
#define LFINGERS 13
#define RIGHTUPPERARM 12
#define RIGHTLOWERARM 11
#define RPALM 10
#define RFINGERS 9
#define PELVIS 8
#define LEFllHlGH 7
#define LEFTCALF 6
#define LHEEL 5
#define LTOES 4
#define RlGHllHlGH 3
#define RIGHTCALF 2
#define RHEEL 1
#define RTOES 0
We abstract just the drawing routine for any part identified by the above-
defined component ID in the following function:
void drawWComponent1intcomponent) {
int i;
-
for li O;i~noofpoly[componentl"3;i -i + 3) {glBeginlGL-TRIANGLES);
gNertex3fvl&lcoords[component][3"indices[componentl[ill~~;
glVertex3fvl&lcoords[componentl[3*indicescomponentl+ 111));
glVertex3fvl&lcoords[componentl[3*indices[componentl+ 211));
glEnd0;)
1
Let us start defining the actual components. For this model, we do not define
an array of transforms; each component has only specific transforms that can be
applied to it. The right leg is composed of the calf as its leaf node (we do not
maintain the toes and heel as further nodes in this hierarchy). The calf can only
rotate about the x-axis (swing back and forth). Since the calf is not defined with
its base at the origin, we do need to translate it to the origin before rotating it (and
translate it back when we are done). This will ensure the correct pivot point
being used for rotation. The component Rightcalf is defined as follows:
The thigh can rotate about both the x- and z- axis for a forward and sideways
swing respectively. The function to draw this component is defined below. In
addition to its own transforms, we also pass in the rotation information for its
child node.
glPushMatrix0;
11 transform the entire android
glTranslatef(Tt01, Tt11, Tt211;
glRotateflR[Ol,1.,0.,0.l;
glRotatefIRt11,0.,1 .,O.l;
glRotateflR[21,0.,0.,1 .I;
11 draw the components and their children
draw-ComponentlROOTl;
{
draw-CornponentKHESTI;
draw-RightAmlrightarmangl;
draw Lefttrrnlleftarmangl;
drawI~eadlheadan~l;
1
draw-RightLeglrightlegangl;
1
glPopMatrix0;
1
In Exarnple5-I 4, we make Android march by continuously changing the rotation
on the arms and legs.
void Displaylvoidl
{
-- -
if (cycle%2 01 {
rightarmang[Ol- 0.3; leftarmang[Ol+ 0.3; -
-- -- -
rightarmang[21--0.4; leftarmang[21+ 0.4;
rightlegang[Ol- 0.6; leftlegang[Ol+ 0.6;
rightlegang[21+ 1.2; leftlegang[21- 1.2;
1 else {
--
rightarmang[Ol + 0.3; leftarmang[Ol- 0.3;
rightarmang[2] + 0.4; leftarmang[21- 0.4;
--
-- --
rightlegang[Ol + 0.6; leftlegang[Ol- 0.6;
rightlegang[21- I.2; leftlegang[21+ 1.2;
1
pos+ +;
if lpos >-I001 {
-
pos 0;
cycle+ +;
1
glClear IGL-COLOR-BUFFER-BIT);
glColor3f 11.0,1.0, 1.01;
glLoadldentill;
draw AndroidlT, R, headang, leftarmang, leftlegang, rightarmang, rightlogang);
g~~~~~T;~~;
glutSwapBuffers0;
glutPostRedisplayll;
1
You can experiment with different kinds of transformations on the Android
to verify that the motion is consistent with the model hierarchy we designed.
Ideally, we would like to move the Android based on some user input. We leave
this part of the code as an exercise for the reader. The entire code can be found
in Exarnple5-14/Example5-14.cpp. You will have to compile and link this
program with the provided files: vrrnl.cpp and vrrn1.h.
Summary
In this chapter, we have learned the basics of 3D modeling. The polygon shape
was used to define primitive as well as complex shapes. We have learned how
principles of transformations can be used to define hierarchical models. By
developing 3D models, defining a viewing position, and a projection, we can
define a 3D world inside our computer. The camera just needs to be clicked to
photograph the world and display it onto the screen. In the next chapter we shall
see how to render this 3D world to generate realistic images.
Chapter 6
Rendering: Shading
and Lighting
In the last chapter, we saw how to model objects and represent them as wire-
frame models. Wire frame models depict the outer hull of the object but do not
convey a realistic image of the model. The next step in our quest for visual
realism is to paint our models so we can render realistic images. This process is
called rendering.
In this chapter you will learn all about the components of rendering:
In most cases, however, we do not want surfaces to be colored with just one
color. Surfaces should appear shaded based on the amount of light that they
receive, as is the case in real life. To be able to simulate this effect, we need to
define material properties of the surface: not only its color but also how it
responds to light. This process is called shading. We need to define light sources
to light up the scene, which enable us to view it-a process called lighting. Once
the shading and lighting of a scene has been established, shading algorithms are
then used to finally render the images.
Rendering is a complex process and can be very time consuming. For
example, a typical image in the film Toy Story took anywhere between an hour
and 72 hours to render on a workstation! Different shading algorithms use
different methods and tricks to simulate the behavior of light and surfaces.
Obviously, which algorithm you use is based on your precise needs. An
interactive game needs shaders whose output may not be very sophisticated but
can be rendered quickly, whereas a blockbuster movie production can afford to
use complex shaders that can take days to render images but produces
spectacular results. We shall employ some simple shading algorithms in this
book and shall introduce you to some more complex ones in Chapter 7.
Let us first start with a more in-depth discussion of hidden surface removal.
6.2 Hidden Surface Removal
In the previous chapter, we looked at wire frame representations of models. The
models we constructed were "see throughy'-we could see through the polygons.
We learned how to eliminate the back-facing polygonal surfaces of our model
using a technique called back-face culling. But what about those surfaces that are
obscured by other objects in front of them as shown in Fig..?
In a wire frame mode, it probably is still okay to see through the first object.
But when we draw our object as a solid surface, we want to see the object that is
in front. Compile and run Example6-1/Example6_I. cpp.
This example shows a solid cone and a sphere. The cone translates back and
forth along the z-axis. Surprise, surprise- even when the cone is behind the
sphere, we still see it being drawn in front! We want not only the back surfaces
of the sphere and cone to be hidden from view, but also the portion of the cone
being obstructed from view by the sphere. The process to remove these surfaces
is called hidden surface removal. Back-face culling is part of this removal
process.
Z-Buffering
One of the simplest techniques to accomplish hidden surface removal is known
as z-buffering or depth buffering. In this process, a buffer in memory called the
z-buffer is used to keep track of the surface that is closest to the eye for any given
pixel. The closest object finally gets drawn onto the screen. Note that for solid
closed objects, this process will automatically remove back-facing surfaces also.
OpenGL provides z-buffering by way of a depth buffer. To use it, you need to
first enable depth buffering. You can do this by initializing the display mode to
use the depth buffer
Before drawing the scene, you need to clear the depth buffer and then draw
the objects in the scene. OpengGL will perform all the calculations to store the
closest surface information in its depth buffer and draw the scene accordingly.
Shown below is a snippet of code to draw a sphere and a cone. The cone is
animated to periodically move in front of and then behind ahead the sphere.
Depth buffering ensures that obstructed parts do not get drawn onto the screen.
The entire code can be found under Example6-I/Example6_I.cpp(you will need
to remove the commented lines to enable the depth test)
glClear(GL-COLOR-BUFFER-BIT I GL-DEPTH-BUFFER-BIT);
-
Tz Tz + Tinc;
-
if (Tz > 51 Tinc -0.5;
-
if (Tz < -5) Tinc 0.5;
glLoadldentity0;
gl~L00kAt(O.,O.,l0,0,0,-100,0.,1.,0.);
glRotatef(-90,1,0,01;
{
glColor3f(l.,O.,O.);
glutSolidSphere(2., 8,81;
When you make a call to glColor, all surfaces drawn after the call will use
the same color. If you see how the cone and sphere are rendered, you will be very
6 RENDERING'
CHAPTER SHADING
AND LIGHTING 135
disappointed. They look so 2D! We would like our surfaces to be shaded to give
a sense of depth.
Surface removal is but one factor in generating a realistic image. Our next
task is to shade the visible surfaces so they appear 3D based on the lights
illuminating them, and their surface material properties. Since we want to
simulate the real world, CG simulation of lighting and shading follows a close
analogy to how lights and surfaces behave in the real world, and how humans
actually see. Let us see how light works in the real world and then draw upon
this analogy in our discussions of CG based lights and materials.
Law of Reflection
Light is usually modeled as light rays (which can be thought of as vectors!).
Many rays traveling together are referred to as a beam of light. Light rays behave
in a very predictable manner. If a ray of light could be observed approaching and
reflecting off a flat mirror, then the behavior of the light as it reflects would
follow the the law of reflecti0n.A~shown in Fig.6.5, a ray of light, I, approaches
the mirror and is called the incident ray. The ray of light, R, that leaves the
mirror, is known as the reflected ray. At the point of incidence where the ray
strikes the mirror, we can define a normal, N, to the surface of the mirror. The
normal is perpendicular to the surface at the point of incidence and divides the
angle between the incident ray and the reflected ray into two equal angles. The
angle between the incident ray and the normal is known as the angle of
incidence. The angle between the reflected ray and the normal is known as the
angle of reflection. The law of reflection states that when a ray of light reflects
off a surface, the angle of incidence is equal to the angle of reflection.
Whether the surface being observed is microscopically rough or smooth has
a tremendous impact upon the subsequent reflection of a beam of light. Fig. 6.6
below depicts two beams of light, one incident upon a rough surface and the
other on a smooth surface.
reflection, they scatter in different directions. The result is that the rays of light
incident to the surface are difhsed upon reflection. This type of reflection is
called diffuse reflection. In practice, both types of reflection can be observed on
the same surface.
When light strikes an object, not only does a portion of it gets reflected, as
discussed above, but it can also be absorbed or transmitted through. Objects have
a tendency to selectively absorb, reflect, or transmit light of certain colors. So,
one object might reflect green light while absorbing all other frequencies of
visible light, causing us to see it as green. Another object might selectively
transmit blue light while absorbing all other frequencies of visible light, causing
it to look blue and translucent.
Light being bounced around tends to decay over time and distance due to
atmospheric absorption. This is why far-away objects appear dimmer than
objects that are closer to us. This phenomenon is referred to as attenuation.
The color and intensity of the reflected light that eventually reaches the eye
is the color that the surface is perceived to have.
Specular
reflection
I Surface
I
Fig.6.7: Four components of Phong reflectance model
CG lights are defined to illuminate the scene. These lights are again modeled
with RGB colors for the ambient, diffuse, and specular components. The red,
green and blue components have a value between 0 and 1. Some software
packages further define an intensity to the light: the intensity is simply a scalc
factor for the color of the light: the actual color of the light is the product of its
color and its intensity. The OpenGL modcl does not use intensity for light sources.
When incoming light strikes an object, each of these four components is
calculated to detcrminc its individual contribution to the reflcctcd light. All four
components then added together to attain the final color of the outgoing light
Let us look into each of these components and how they are calculated.
Ambient Reflectance
Ambient reflectance is actually just an approximation to the fact that there is
never a perfect dark room in this world. Some amount of light always seeps in,
enabling vision. This kind of illumination enables us to see faint traces of objects
with flat shades of surface colors and is refcrred to as ambient lighting.
When light with an ambient component (ambient light) strikes a surface with
ambient reflectance, it is scattcrcd equally in all directions with a constant
multiple, causing the surface to look flat as shown in Fig.6.9.
CI-~AP'IEIH6 RENDERING.
SHADING
AND 1-IGHTING 139
Let us see how to render a sphere using ambient lighting. In openGL, we can
specify global lighting. Global lighting is not associated with any light source,
but is prevalent uniformly in the 3D world being defined. The command to set
up a global ambient light is
In Example6-2, we use global ambience to light up a sphere. The init function defines
the lighting model as ambient and sets the ambient color to a dull white, as shown below.
glLightModelfvlGL-LIGHT-MODEL-AMBIENT, light-ambient);
We need to enable lighting in order for it to affect the rendering as shown below
We also need to define a material for the sphere. To set material properties of an
object, we use the OpenGL command
This command specifies a current material property for use in lighting calcu-
lations.
face can be GL-FRONT, GL-BACK, or GL-FRONT-AND-BACK to
indicate which face of the object the material should be applied to.
pname specifies the particular material property being (such as
GL-AMBIENT for ambient reflectance)
param is the desired value(s) for the property specified.
In our example, we set the ambient reflectance of the material such that it
reflects all the red component of incident light and absorbs all of green and blue
by defining the material as
-
GLfloat mat-ambient0 { 1, 0.0,0.0, 1.0);
glMaterialfviGL-FRONT, GL-AMBIENT, mat-ambient);
Since it reflects only the red component of light, the material will look red.
Indeed, you can see the analogy between the color of the material and its
reflectance coefficients. Surfaces are rendered using the most recently defined
material setting. Since we will only be setting one material for this example, we
can set it in the init() finction itself. The display code is as follows:
void Display(void)
{
glClear (GL-COLOR-BUFFER-BIT I GL-DEPTH-BUFFER-BIT 1;
I1 material as already been set in the init function
glutSolidSphere(2., 40,321;
glhsh(l;
1
Remember to always turn on depth buffering when lighting 3D scenes. This
will ensure you are always viewing the surfaces that are closest to the camera!
Run the program. Notice how the sphere appears as a dull flat colored red ball.
The sphere looks flat because it only has ambient reflection defined. The
entire code can be found under Example6-2/Example6-2.cpp.
c l - 1TER
~ ~6 RENDERING.
SHADING
AND LIGFITING 141
Diffuse Reflectance
Surfaces with diffuse reflectance scatter light equally in all directions. The
amount of light reflected is directly proportional to the angle of incidence of the
incoming beam of light. Diffuse reflectance can be observed on dull objects such
as cloth or paper.
I Surface
I
Fig.6.10: Diffuse reflectance
The exact math to calculate diffuse reflection was proposed by Lambert and
so this reflectance is often referred to as Lambert reflectance. If we assume the
light to have a diffuse color of (Rid, Gid, Bid) and the surface to have a diffuse
reflection coefficient of (Rd, Gd, Bd). then the diffuse color component of the
reflected light can be calculated as
where 6 is the angle between the incident ray (direction of light source) and the
normal to the surface at the point of incidence. So if 8 is 0 (the light hits the
incident light
Let us apply ambient and diffuse reflectance to our famous snowman from
Chapter 5. In Example6-3, we define all parts of Snowy to have the same
ambient reflectance. The ambient color is slightly blue and is defined in the top-
level function, draw-Snowman as
-
GLfloat mat-ambient0 { 0.1,0.1, 0.2, 1.0 );
glMaterialfv(GL-FRONT, GL-AMBIENT, mat-ambient);
The diffuse components are different for each part. The snowballs have a whitish
diffuse component:
GLfloat eye-diffuse0
GLfloat nose-diffuse0
--{0.0,0.0,0.0,1.);
{0.9,0.5,0.0,1.);
GLfloat hand-diffuse0 - {0.5,0.3,0.1,1.);
Surfaces are rendered using the most recently defined material setting.
Hence, we assign individual materials to Snowy's parts in their local drawing
routine. For example, the draw-Bottom function is now defined as
void draw-BottomlGLfloat "botXforms){
glMaterialfv(GL-FRONT, GL-DIFFUSE, snow-diisel;
We now need to define a light with a specific direction to see the effect of
difise reflectance. The OpenGL command to specify a light source and its
properties is either glLightf for nonvector proprties or glLightfv for vector
properties:
GLfloat light-ambient0
-
- { 0.5,0.5,0.5,1.0 );
GLfloat light-diffuse0 { 0.8,0.8,0.8, 1.0 );
glLightfv(GL LIGHTO, GL-AMBIENT, light-ambient);
g l ~ i g h t-f v ( ~ ~ GL-DIFFUSE,
- ~ l ~ ~ ~ ~ ,light-diffuse);
Note that the position and direction of lights are transformed by the
modelview matrix! Let us make our graphics more interesting by having the light
rotate about the sphere. To do this, we define the position of the light source in
the Display function (within the Model space), and constantly spin it around.
The spin variable increments at every tick. The entire code can be found under
Example6-3, in files Example6-3 cpp, Snowman.cpp and Sn0wman.h.
Voila! You will see a snowman that is lit up by a rotating light source
Specular Reflectance
Specular reflection produces shiny highlights often seen on shiny objects like
glass or metal. Specular reflection, unlike ambient and diffise reflection depends
on both the direction of the incident light as well as the direction that the surface
is being viewed from. The reflection is brightest when the viewing direction is
parallel to the reflected light.
The equations to define spccular reflectance have been proposed by many
mathematicians. Each formula simulates the specular reflectance slightly
differently. However, in general, specular reflection depends on three components:
I .surface orientation, N
2.direction of light source, I
3. viewing direction, V
Assuming a function specular that depends on the above three factors, the
specular reflection color can be defined as
RI, ,G, )B, -IRis*R<specular0, GkG<specularO, BkB<specularlll
viewpoint
I Surface I
Fig.6.13: Specular reflectance
6 RENDERING
CHAPTER SHADING
AND LIGHTING 145
-
GLfloat light-speculadl {0.8,0.8,0.8, 1.0);
glLightfvlGL-LIGHTO, GL-SPECULAR, light-specularl;
The material for all Snowy's components have a common specular reflective
component, defined as
-
GLfloat mat-speculadl {1.0,1.0,1.0,1.0);
glMateriahlGL-FRONT, GL-SPECULAR, mat-specularl;
OpenGL allows you to control the size and brightness of the highlight using
the parameter: GL-SHININESS. You can assign a number in the range of [0.0,
128.01 to GL-SHININESS-the higher this value, the smaller and brighter
(more focused) the highlight. We define our material to have a shininess defined
as
-
GLfloat low-shininess0 (20);
glMateriahlGL-FRONT, GL-SHININESS, low-shininess);
When you run the program, you will see a shiny snowman as shown in
Fig.6.15
Emission
The emissive color of a surface adds intensity to the object, but is unaffected by
any light sources.
-
GLfloat mat-emission0 {0.5,0.0,0.0,0.0);
void draw-NoselGLfloat *noseXforms){
glMaterialfvlGL FRONT, GL-DIFFUSE, nose-diffuse);
g l ~ a t e ~ i-a l f v l ~GL-EMISSION,
~ - ~ ~ ~ ~ ~mat-ernissionl;
,
When you run the program, you will see a glow in the dark nose. Scenes can be
pretty boring with just one kind of light illuminating them. In real, we see lights
of all kinds--distant lights such as the sun, spotlights such as light emanating
from a light bulb or those used in theater. All these lights help to add to the three-
dimensionality of our world. Let us explore what more OpenGL can provide to
us in terms of lights.
6' RENDERING'
CHAPTER SHADINGAND LIGHTING 147
OpenGL Lights
OpenGL can assign many more attributes to lights than just their color. Lights
can be defined with a position, a direction, and even a cone of illumination etc.
The most commonly used types of lights in OpenGL are
Distant (directional) light
Point light
Spotlight
Distant Light
The best example of a distant light is the sun. As viewed from any one position
on the earth, its rays are essentially parallel, and the position makes no practical
difference on the intensity of the light. In Fig.6.17 a distant light is shown light-
ing up a sphere. The distant light is represented by directed lines to indicate
which direction the light is coming from. Notice how the side of the sphere
facing away from the light is completely black.
Point Light
A bulb is a good example of a point light. A point light source distributes light
through space from a single point. It distributes beams of light evenly in all
directions. The intensity of the light is usually defined to fall off with the square
of the distance from the light to the surface. You could, however, define the
intensity to have no fall off at all. A point light needs a position to be completely
defined, and is often referred to as positional light.
Fig.6.18: Point Light
In Example 6-4, we define a light source located at (-1,0,0) to light up a red
sphere (which is located at the origin). To define the position of the light, we set
the vector of (x,y,z, W)values for the GL-POSITION parameter. If W is nonzero,
the light is positional, and the (x, y, z) values specify the location of the light in
homogeneous object coordinates. This value is transformed by the model-view
matrix, so be careful where and how you define it!
I1 position of light
-
GLfloat light-position0 { -1.0, 1.0,0.0, 1.0 );
glLightfvlGL-LIGHTO, GL-POSITION, light~ositionl;
gluLookAtlO.,0.,5, O,O,-100,0.,1.,0.~;
The color of the light is defined in a manner similar to what we discussed earlier.
The entire code can be found in Example6-4/Example6-4.cpp.Try changing the
position of the light to see the results.
Spotlight
Theater lights or bulbs enclosed in a lamp shade are good examples of spotlights.
A spot light is like a point light, but its light rays are restricted to a well defined
cone. This kind of light is often used to direct the viewer's eyes to certain parts
of the scene. In OpenGL, you can use the GL-SPOT-CUTOFF parameter to
specify a light as a spotlight with the specified angle between the axis of the cone
and a ray along the edge of the cone. The angle of the cone at the apex is twice
this value, as shown in Fig.6.19. No light is emitted beyond the edges of the
Fig.6.19: A spotlight
6 KRENDERING
CI~APTE SIIADINGAND LIGHTING 7 49
cone. The value for GL-SPOT-CUTOFF is restricted to being within the range
[0.0,90.0] (unless it has the special value 180.0). The following line sets
GL-LIGHT0 to be a spotlight with the cutoff parameter at 20 degrees:
glLightf(GL-LIGHTO, GL-SPOT-CUTOFF, 20.0);
You also need to specify a spotlight's position and direction, which
determines the axis of the cone of light:
-
GLfloat IightgosiiionU { -1.Or 1.Or 0.0, 1.0 );
-
GLfloat spot-direction1 { 0.0,0.0, -1.0 );
glLightfvIGL LIGHTO, GL-SPOT-DIRECTION, spot-direction);
g l L i g h t f-v ( ~ ~ - ~GL-POSITION,
l~~~~I lightgositionl;
Fig.6.20: GL-SPOT-CUTOFF
Attenuation
For real-world lights, the intensity of light decreases as distance from the light
increases, a phenomenon known as attenuation. Since a directional light is
infinitely far away, it doesn't make sense to attenuate its intensity over distance,
so attenuation is disabled for a directional light. However, you might want to
attenuate the light from a positional (point or spot) light. To do this, you can
define an attenuation factor to your light. (OpenGL has three different factors for
attenuation. Here, we just look at the constant attenuation factor).
Refer to SHRE03 for more details on attenuation and how to set attenuation
factors in OpenGL.
150 6 4 CG. RFFLECTANCF
MODEL
I Surface
I
Fig. 6.21: Adding all four components
the scene blend with their background, to enhance the mood of the scene, and to
make the scene eye catching. In Example6-6, we put Snowy against the
backdrop of the Alps. (Refer to Color Plate 2.) To make Snowy blend into the
scene, we need to identify and match the lighting of the background scene. The
scene seems to have a main light coming from the top right hand side. Although
Incoming
light
--
GLfloat IightO-ambient0 { 0.5,0.5,0.5, 1.0 );
GLfloat IightO-diffuse0 { 0.8,0.8,0.8,1.0 );
GLfloat light0-speculafl-{ 0.2,0.2,0.2,1.0 );
You may have noticed that both diffuse and specular reflection depend on the
normal vector to the surface. How and where is this normal vector being defined?
We saw in Chapter 5 that the normal vector to a polygon (or to a vertex on
the polygon) determines the orientation of the polygon. This normal vector can
be used to determine front- vs. back-facing polygons: polygons: with normal
vectors facing away from the viewpoint are back-facing and are culled from the
scene, as shown in Fig.6.23. The normal vector also determines the orientation
of the polygon or to a vertex on the polygon, relative to the light sources. This
normal vector is used in the lighting calculations we looked into earlier. In
OpenGL, we can define the normal vector by using the commands
+
Fig.6.23: Front- and back-facing polygons determined by the direction of normal vectors
glNormal3l Gltloat nx, Glfloat ny, Glfloat nz);
void glNormal3fvlconst GLfloat *v);
This command sets the current normal to the value of the argument passed in.
Subsequent calls to glVertex"0 cause the specified vertices to be assigned the
current normal. For the glutSolidObject functions that we use, the normal vectors
are already identified within the routine.
In Example6-7, we read in the VRML model of the android by using our
ReadVRML function. Recall that this function also reads in the normal vectors
to the vertices of the polygons being defined.
In the Display function we assign normals to the vertices as shown:
glBeginlGL-TRIANGLES);
glNormal3fvl&lnormals[j][3*nindices[jl[ill)~;
glVertex3fvl&lcoords[jl[3*indices~lFll~~;
glNormal3fvl&lnormals[j][3*nindices[j][i+ I]]));
glVertex3fvl&lcoords~[3*indices[jl[i+ 111));
glNormal3fvl&lnormals~[3*nindices[j~ + 211));
glVertex3fvl&lcoords[jl[3*indices[jl[i + 211));
glEndl);
We also assign a shiny yellow material to the android. For each vertex,
OpenGL uses the assigned normal to determine how much light that particular
vertex receives and reflects, in order to generate the image. Run this program to
see the Android in all its metallic glory!
In the last section, we learned how to determine the color of a surface. For
every surface point, the four shading components are added together for each
light in the scene. This value is then clamped to the maximum intensity of 1 to
arrive at the final color of the point.
Performing this calculation for each surface point can be very tedious.
Consequently, shading models are used that limit the calculations to certain key
points on the surface. The surface is then colored by some average of these
values. For polygonal surfaces, the key points are the vertices of the defining
polygons.
Notice how flat shading causes a sudden transition in color from polygon to
polygon. This is because every polygon is rendered using a normal vector that
changes abruptly across neighboring polygons. Flat shading is fast to render, and
is used to preview rendering effects or on objects that are very small.
Gourad Shading
Gourad shading is used when smooth shading effects are needed. In this process,
each vertex defines a normal vector. This normal value is used to calculate the
color at every vertex of the surface.
The resulting colors are averaged into the interior of the surface to achieve a
smooth render. Since polygons share vertices (and hence the same normal value),
Camera
S
Fig.6.28: A texture image, repeated along t and clamped along s
should be used and how they should be averaged or interpolated. There are a
number of generalizations to this basic texture-mapping scheme. The texture
image to be mapped need not be two-dimensional: the sampling and filtering
techniques may also be applied for both one- and three-dimensional images. In
fact 1D texture mapping is a specialized version of 2D mapping. The texture may
not be stored as an array but may be procedurally generated. Finally, the texture
may not represent color at all but may instead describe transparency or other
surface properties to be used in lighting or shading calculations.
Mapping Schemes
The question in (2D) texture mapping is how to map the two dimensional texture
image onto an object. In other words, for each polygonal vertex (or some other
surface facet) in an object, we encounter the question, "Where do I have to look
in the texture map to find its color?"
coordinate space. For example, for a map shape that is planar, we take the (x,y,z)
value from the object and throw away (project) one of the components, which
leaves us with its two-dimensional (planar) coordinates. We normalize the
coordinates by the maximum extents of the object to attain coordinate values
between 0 and 1. This value is then used as the (s,t) value into the texture map.
Figure 6.30 shows such a mapping. Let us see how to specify texture maps in our
openGL environment. First, we need to specify a texture. The OpenGL command
to specify a two-dimensional image as a texture is
The width and the height of the image must be a power of 2. We shall look
into the arguments of this command shortly. To set the magnification and
minification filters in openGL, you use the command
right before you make the glVertex call. Note that all 2D texture commands have
a corresponding 1D call. You also need to define the texture mode to be used
while rendering. GL-MODULATE modulates the current lighting and color
information with the texture image. GL-DECAL uses only the texture color. You
can set the texture mode using the glTexEnvi function:
In Example6-8, we define the planar texture for the three models. In the init()
function, we first generate and bind a new texture:
We then set the various wrap and filter parameters for this texture:
glTexParameterilGL-TEXTURE-2D,GL-TEXTUREEWRAPPS GL-CLAMP);
glTexParameterilGL TEXTURE 2D,GL TEXTURE WRAP TI GL CLAMP);
g l ~ e x ~ a r a m e t e r i l ~ ~ ~ G~ ~E TXETX~T ~U ~R ~ M2 ~A G
I > I L T EGL-NEAREST);
~
glTexParameterilGL-TEXTURE-2D, GL-TEXTURE-MI N-FILTER, GL-NEAREST)
Environment Mapping
If you look around your room you may realize that many objects reflect their
surroundings. A bottle of water, a mobile phone, a CD cover, a picture frame, etc.
are only a few examples of reflecting objects that could be found in any 3D
scene. To make the 3D world more realistic, objects should show reflections of
their environment. This reflection is often achieved by a texture-mapping
method called environment mapping.
The goal of environment mapping is to render an object as if it were reflec-
tive, so that the colors on its surface are those reflected from its surroundings. In
other words, if you were to look at a perfectly polished, perfectly reflective silver
object in a room, you would see the walls, floor, and other objects in the room
reflected off the object. (A classic example of using environment mapping is the
evil, morphing Cyborg in the film Terminator 2.) The objects whose reflections
you see depend on the position of your eye and on the position and surface angles
of the silver object. Of course, objects are not usually completely reflective, so
the color of the reflection is modulated with the object's actual color for a
realistic look.
True environment mapping can be achieved using ray tracing, a technique we
shall learn about in Chapter 7. Ray tracing is a very expensive option and is
usually not necessary for most cases. More often, certain tricks are used to
Right
u Bottom
to get the current model view matrix and apply the necessary matrix operations
on your vertexlnormal data. Interested readers should try using this setup to map
the cube environment onto our marching android from Chapter 5.
Summary
In this chapter, we have studied the important techniques used in rendering. Our
quest for visual realism has taken us from hidden surface removal to lighting and
materials to texture mapping of surfaces. These techniques approximate the
physics of sight to generate photorealistic images. We shall look into some more
advanced rendering algorithms in the next chapter.
Chapter 7
Advanced Techniques
Graphics scenes can contain different kinds of objects: trees, flowers, glass, fire,
water, etc. So it is not too surprising that numerous techniques can be applied to
depict the specific characteristics of each object. In the last few chapters, we
learned about the basic principles of modeling and rendering. In this chapter, we
shall look into some advanced techniques popular in the CG world.
In Chapter 5, we dealt with linear components (lines and polygons) to depict
the hull of models. For complex models, polygonal surfaces can be tedious to
build for and may also occupy large amounts of storage space. Curves and sur-
faces are often used instead in order to achieve smoother representations with
less storage space. In this chapter, we look into several kinds of curves and
surfaces used in CG. Subdivision surfaces is another technique that has become
popular to achieve a high degree of local refinement.
In Chapter 6, we saw some basic rendering techniques. Although we went
from wire-frame to real shaded images, we are still not close to the photo-
realistic images that we set out to produce. Our images don't have shadows or
transparency, for example. In this chapter, we shall also talk about an advanced
algorithm called ray tracing to achieve such effects. Radiosity is another
rendering technique that we shall discuss briefly.
In this chapter, you will learn
w Advanced modeling
Curves-Bezier, B-splines, and Nurbs
Nurbs surfaces
Other techniques
Advanced rendering algorithms
Ray tracing
Radiosity
7.7 Advanced Modeling
Numerous techniques are used to model objects in CG. Which technique is used
depends on the object being modeled, as well as the final outcome desired.
Polygonal models are quick and easy to display but do not necessarily depict a
smooth surface. They are used widely in the gaming industry for speed of
display. Other techniques can depict smooth hulls but are slower to display. One
such technique is the use of parametric curves and surfaces.
Parametric Curves
Curves are used in CG for all kinds of purposes-fonts, animation paths and
modeling, to name a few. It is instructive to look into the mathematical equations
used to represent curves to understand how they are used in CG.
Parametric Equations
If you recall high school algebra, the implicit function for a curve in 2D space is
of the type:
fky) =0
For example a circle with radius r centered at 0 has a defining equation
x2+yL2=o
The different kinds of curves (and surfaces) are classified by the coefficient
values of the polynomial equation. The coefficient values determine how the
curve interpolates the control points. Some of the commonly used curves are
Bezier, B-Spline, Hermite, etc. The two kinds of curves we shall study in this
book are Bezier curves and B-splines (specifically Nurbs).
x(t)
Fig.7.3: Curve segments meeting at a join point
Since a curve is approximated by more than one curve segment, it is
important to understand how the curve segments behave at the point at which
they meet which is called a join point. If two curve segments of a curve meet
together, the curve is said to have CO continuity. Usually, a curve should at least
be CO continuous to define a continuous curve! If in addition the tangent vectors
of the two curve segments (the first derivative, also called as velocity of the
curve) are equal at the join point, the curve is said to be CI continuous. If the
curvature of the two curve segments (second derivative, also called rate of
change or acceleration of the curve) are also equal at the join point, then the
curve is said to be C2 continuos as shown in Fig.7.3. C1 continuity is required
in curves to guarantee a smooth curve. C2 continuity guarantees a smooth
transition between segments.
Splines and, in particular, Nurbs have C1 and C2 continuity at their join
points and are often preferred over other types of curves.
Bezier Curves
The (cubic) Bezier curve segment has four control points: two control points, P1
and P4, define the endpoints of the curve, and the two control points, P2 and P3 Y
affect the shape of the curve by controlling the end tangent vectors.
GLfloat ctrlPoints[41[3] -
{-6.10.10.1
-4.,6.,0.,
4.,-6.,0.,
6.,0.,0.);
-
int numPoints 4;
The control points in the cubic map are defined by:
glMaplf(
GL-MAPI-VERTEX-3, I1 target, type of control data, 3 coordinates for each vertex
o., 11 lower t range
I., I1 upper t range
3, I1 stride
numPoints,&ctrlPoints[01[01~;
The next step is to determine the domain values to be used to evaluate the
polynomial. OpenGL works with the concept of grids to specify evenly spaced
domain values for evaluation. The command
tells openGL to lay a grid of 20 evenly spaced points along the t domain. Finally,
we need to instruct OpenGL to evaluate the curve at the defined grid points. This
is accomplished by the command
All the evaluated points are then smoothly connected using the primitive
specified (in this case, a line). You also need to specify the first and last integer
values for grid domain variable t. In this case, the curve will be evaluated and
plotted at t =(0,1/20,2/2- ... 1).
The more the number of points in our grid, the smoother will be the final
curve that is output. When you compile and run Example 7-I/Example7-l.cpp,
you will see the Bezier curve, defined by the control points specified.
Try changing the number of grid points to see how this affects the
smoothness of the curve. Note that this only changes how many points are used
to evaluate the polynomial equation-it doesn't change the equation being used.
B- Splines
Spline curves originated from flexible strips used to create smooth curves in
traditional drafting applications. Much like Bezier curves, they are formed
mathematically from piecewise approximations of cubic polynomial functions.
B-Splines are one type of spline that is perhaps the most popular in computer
graphics applications. The control points for the entire B-spline curve are defined
in conjunction. They define C2 continuous curves, but the individual curve
segments need not pass through the defining control points. Adjacent segments
share control points, which is how the continuity conditions are imposed. For
this reason, when we discuss splines, we discuss the entire curve (consisting of
its curve segments) rather than its individual segments which must then be
stitched together. Cubic B-splines are defined by a series of (m=n+l) control
points: Po, P1...Pn
Each curve segment of the spline Qi 3 5 i,< n
is defined by the four control points Pi-3,Pi-2,Pi-1,Pi.
For example, in Fig.7.6, we show a spline with m=8 control points. The
individual segments of the curve are Q3, Q4, Q5, Q6 and Q7. Q3 is defined by
the four control points, PO-P3,Q4 by points PI-P4etc.
Conversely, every control point affects four segments. For example, in the
P2 P5 '6
I I
Control
Knot
I I
Q5 P4 P7
P3
above figure, point P4 affects segments Q3, Q4, Q5, and Q6. Moving a control
point will affect these four segments but will not affect the entire curve. This is
a very useful property of B-splines that we shall look into more in the next
few chapters.
The join points between the segments are called knots. The knot between
segment i and i+l is represented as ki.The initial point of the first segment and
the endpoint of the last segment are also called knots, so there is a total of (n-I)
knots for the spline under consideration. When knots are uniformly placed, as
shown in Fig.7.6, the curve spline is called a uniform non-rational spline.
Unfortunately, it is difficult to define and control the splines since the segments
do not interpolate the control points.
Non-uniform non-rational B-splines define (n+5) knots. The knots need not
be uniformly spaced-and in fact are user defined. The advantage is that we can
force the curve to interpolate certain control points. Non-uniform B-splines uses
the notion of knot value sequences: a non-decreasing sequence of knot values
that defines the placement of knots of the curve. For example, if we assumed the
curve above was a non-uniform non rational B spline, its knot sequence would
effectively be (0,1,2,3,4,5,6,7,8,9,10,11). (n+5 = 12 knots)
If successive knot values are equal in the sequence, it is called a multiple
knot. Multiple knots causes the curve to approximate the associated control point
more closely. In fact, three successive knot values forces the curve to actually
interpolate the control point, thereby making the shape of the curve easier to
define. Defining multiple knots does lead to a loss in continuity, but only at the
associated control point. If we modified our curve above to assume a different
knot sequence, the results would appear as shown in Fig.7.7.
Po.'-"LJ'-
Triple knot p7
Control Point
Knot
Fig.7.7:Non-uniform non-rational B-splines. Multiple knots, double knot
with knot sequence (0,1,2,3,4,4,5,6,7,8,9,10) causes C1 continuity,
whereas a triple knot with knot sequence ( 0 , 1 , 2 , ~ , ~ , ~ , ~ , ~ ,results
6,~,8,~)
in only CO continuity.
Nurbs
B-splines (or any other non-rational curve) can be defined in homogenous
coordinates by adding W(t)=l as the fourth element in the parametric equation;
Q(0 = (X(t), Y(0, Z(t), W(t)). (As usual, moving from homogenous coordinates to
3D space involves dividing by W(t)). This process is called rationalizing the curve.
The advantage of rational curves is that they are invariant under rotation, scaling,
translation and perspective transformations. This means we need to apply the
transformations only to the control points and then re-evaluate the curve equation
to generate the transformed curve. A non-uniform rational B-spline is also called a
Nurb and is heavily used in CG due to the properties described above.
In Example7-2, we draw a Nurbs curve using OpenGL. We first need to cre-
ate a new Nurbs renderer before we can define the actual curve. This is done by
using the command
We can set properties of this nurb as follows
This property means that the resultant Nurbs curve will be drawn using a line
primitive. We define a set of eight control points (n= 7)
GLfloat ctrlPoints[81[31
{-5.,-5.,0.,
-
-5.,5.,0.,
0.,5.,0.,
O.,-5.,0.,
5.,-5.,0.,
8.,5.,0.,
12.,5.,0.,
12.,-5.,0.);
GLfloat Knotsl[l21
GLfloat Knots2[121
-- {0,1,2,3,4,5,6,7,8,9,10,11);
{0,1 ,2,3,4,4,5,6,7,8,9,l 0);
GLfloat Knots3[121 - {0,1,2,3,4,4,4,5,6,7,8,9);
The OpenGL command to define and display a Nurbs curve is
The parameters for this fimction are similar to the parameters we set for
glMap but includes support for defining the knot sequence.To define the curve,
we bracket it between gluBegnCurve and gluEndCurve.
gluBeginCurvelpNurb);
gluNurbsCunre(pNurb,
12, /Ithe number of knots
Knots1, I/the knot value sequence
3, //stride
&ctrlPoints[Ol[Ol, /Ithe control points
4, /Ithe order of the curve
GL-MAPI-VERTE-3); /I type of vertex data
gluEndCurve(pNurb);
The order of the NURBS curve equals the degree of the parametric
equation+l; hence a cubic curve has an order of 4. The entire code can be found
in Example7-2/Example7-2.cpp. Try experimenting with different knot value
sequences to see what results you get. Interested readers are encouraged to refer
to DEB001 for more details on splines.
'4 1
e
must be collinear
Boundary
Patches Patches
I1 s t lx,y,zl
GLfloat ctrlPoints[7][41[31 -
{{{-12rollo}r{-12ro15}l{-12rol-5}r{-12101-lo}}l 11s-0
{{-810110}1{-8r0r5}r{-8.r0.1-5.}1{-8.101-10}}1 11s-I
{{~4-~0-~10~}~{~4~0~5}~{~4r0~~5}~{~4~0~~10}}~/1~~2
{{O.,O.,l 0.~,{0,0,5}1{-0,0,-5}I{0101-1
0}}IIIs-3
{{4-~0~~10~)~{4~0r5}r{4r0~-5}~{4~0r~10}}J1~~4
{{8.,0.,10.},{8,0,5}r{8rOr-5}I{8101-10}}Jl~-5
{{12.,0.,1 0.}l{1210r5}r{1210r-5}l{12101-10}}ll~-6
1;
The knots along s and t are defined as
--
GLfloat KnotstJ81 {0.,0.,1.,1.,2.,2.,3.,3.};
GLfloat Knotss[lll {0,0,0,0,1.,2.,3.,4.,4.,4.,4.};
The knot sequence defines a surface that interpolates the first and last set of
control points along the s domain, as shown in the Fig.7.11.
This function has parameters similar to the curve but needs parameters against
two domains, s and t.
gluBeginSurFacelpNurb1;
gluNurbsSurface(pNurb,
11,Knotss,
E,Knotst,
4*3, llstride along s
3, //stride along t
&ctrlPoints[O1[O1[Oll
4, llorder along s domain
4, llorder along t domain
GL MAP2-VERTEX-31;
gl&d~urfacelp~urbl;
We can further enable lighting for the Nurbs surface, by defining lights and
material settings in the scene. We define our Nurbs surface to have a blueish
material and set up some default lights. In order to render the surface, we need
to enable automatic normal vector calculations for the surface by calling:
CSG Models
CSG attempts to build objects by adding and subtracting basic shapes to generate
more complex shapes. For example if we takc a cube and subtract a sphere from
it, we could define a bathroom sink model as shown
Many more modeling techniques exist. Particle systems arc used hcavily to
model particle-based objects like fire or water. Another commonly used
tcchnique to get the model into thc computcr is to scan it in using a scanner. The
scanner digitizes the important features of the model and records these as data
points. Thcsc points can then be used to generate component polygons or be
approximated into a Nurbs surface. Each technique is usehl in its own way and
has its own drawbacks. You should pick thc tcchnique most suited to your
situation at hand.
Fig.7.13: CSG to define a sink
And now, let's look a little bit more into some advanced techniques to render
photorealistic images.
Ray Tracing
Conceptually, ray tracing is a simple process. We simply need to trace the path
of all the light rays in the scene, bouncing them around along the path would
follow in real life (based on the physics of light we learned about earlier). We can
then combine the color of all the rays which strike our image plane to arrive at
the final color for each pixel. (Contrast this approach with that used in Chapter
6, where we only used rays emanating from a light source that got reflected and
directly bounced into the eye.) Fig. 7.14shows a scene of a room with a table, a
martini glass, a mirror, and two sets of lamps. Rays from the lights bounce
around the room to illuminate it.
Fig.7.14: Light rays bouncing in a room
Let us trace the path that some of these rays would actually follow in real life.
Ray A from the lampshade, strikes the wall and is absorbed by the wall. Its
contribution to the scene ends here. Ray B hits the mirror and is reflected into
the image plane to finally enter the eye. We should see the image of the lamp at
this pixel. Ray C is transmitted through the glass, reflects from the table, and is
again reflected into the image plane. We should see the table color with the color
of the reflected image of the glass at this pixel. In general, the light rays leave
the light source and bounce around the scene. The various reflections and
refractions of the rays cause the color of the ray to change. Only the rays that hit
the image plane, however contribute to the image. The color of the ray when it
strikes the image plane will be the color contributed to the image. Some areas are
never lit because rays from the light are obstructed by another object, causing
shadows. Color Plate 5 shows a ray traced image of such a scene generated using
Maya. We have just been ray tracing; we have followed the path of the rays as
they bounce around the scene. More specifically, we have been forward ray
tracing, tracing the path of light starting from their origin. Forward ray tracing is
very inefficient, as most of the rays do not actually contribute to the image.
Tracing backward from the viewpoint to the light sources increases the
efficiency of the process by making sure that we only trace the paths of those
rays that finally reach the eye. In fact, ray tracing always refers to backward ray
tracing.
The process of backward ray tracing works as shown in Fig.7.15. For every
pixel of the image plane, a ray is drawn from the eye through the pixel and then
intersected with models in the scene. Each time a ray strikes a surface, additional
rays are drawn based on the physic of light. If the surface is reflective, a reflected
ray is generated. If the surface is transmissive, a transmitted ray is generated.
Some surfaces will generate both kinds of rays.
In Fig.7.15 we show one such ray striking the table. The table is a reflective
surface. We reflect this ray backward, to strike the martini glass. This ray will
produce the reflected image of the glass on the table surface. The glass is
transmittive as well as reflective, so two kinds of rays can be spawned at this
point, causing reflections in the glass as well as the "see through" appearance of
Fig.7.15: Backward ray tracing
the glass. At every point of contact, a shadow ray is drawn from the point of
intersection to each light source in the scene. If the shadow ray strikes another
surface before hitting the light, the surface of intersection is in the shadow of the
obstructing model. In our case, the point of contact of the ray with the table is in
the shadow of the second glass from Light la. This will cause a shadow of the
second glass at this point.
Mathematically, all these rays with material characteristics of the models tell
the computer about the color and intensity for every pixel in the image. After a
few bounces, the rays are so weak they don't contribute much to the scene. To
avoid infinitely long computations, we usually we set a limit to how "deep" we
trace the rays. For example, we may set the trace to be only 3 levels deep. We
would then stop tracing the rays further. We shall actually generate ray-traced
images in the next chapter. Refer to [GLAS89] for more details on ray tracing
and how to implement your own ray tracer.
Radiosity
Simple recursive ray tracing has many unquestionable advantages. It does a great
job of modeling shadows, specular reflection, and (dispersionless) refractive
transparency.
Unfortunately, ray tracing also has a number disadvantages:
w Some ray tracing methods are extremely computationally expensive and
require much time to produce an image.
w The rendering time is greatly affected by the number of textures and
light sources and by the complexity of environment.
w Ray tracing is view dependent, which means that the all pixel values
have to be re-calculated for every new position of the observer.
To satisfjr demand for generating photorealistic images, without ray tracing's
imperfections, computer graphics researchers began investigating other
techniques for simulating light propagation. In the mid-1980s, radiosity, as this
technique is called, was finally developed.
The radiosity method of computer image generation has its basis in the field
of thermal heat transfer. Heat transfer theory describes radiation as the transfer
of energy from a surface when that surface has been thermally excited. This
encompasses both surfaces which are basic emitters of energy, as with light
sources, and surfaces which receive energy from other surfaces and thus have
energy to transfer.
This "thermal radiation" theory can be used to describe the transfer of many
kinds of energy between surfaces, including light energy.
As in thermal heat transfer, the basic radiosity method for computer image
generation makes the assumption that surfaces are diffuse emitters and reflectors
of energy, emitting and reflecting energy uniformly over their entire area. It also
assumes that an equilibrium solution can be reached; that all of the energy in an
environment is accounted for, through absorption and reflection.
In an enclosed environment conservation of energy assures that the energy
leaving a given surface is incident upon other surfaces, thus a system of
equations (the radiosity equations) may be formed describing the
interdependence of the surface energies.
All that is needed to calculate these equations is the geometry of the scene,
the energy of the light sources (measured in radiosity units, energy per unit area
per unit time), and the diffuse reflectivity at every point on every surface in the
scene.
It should be noted the basic radiosity method is viewpoint independent: the
solution will be the same regardless of the viewpoint of the image and needs to
be calculated only once per scene.
Like ray tracing, radiosity equations produce stunning images. Refer to
[DUTR03] for the latest in radiosity technology. Radiosity is able to very
effectively handle a phenomenon called color bleeding: where one surface's
color seems to bleed onto neighboring surfaces. However, it operates at the cost
of high memory usage and also cannot account for specular highlights since it
assumes that every surface is a diffuse reflector. Probably, in the future, we shall
see techniques that combine ray tracing with radiosity to achieve even more
stunning effects.
Summary
In this chapter, we have learned some advanced techniques to model and render
CG scenes. We have used OpenGL to define and display cubic curves and
surfaces.
In the next chapter we introduce you to Maya. Using Maya, we will model a
3D scene and render it using ray tracing
Chapter 8
And Finally, Introducing
Maya
You may have realized how difficult it is to visualize 3D space in your mind or
on a piece of paper. You may be thinking of creating cool models, but you don't
know where to position the points to get the model of your dreams! Never fear
modeling software is here.
Modeling software was developed to aid in the 3D visualization process for
modeling. Most 3D software now have a toolkit for rendering and animation as
well.
The 3D software package we discuss in this book is called Maya. Maya has
been developed by a company called AliasIWavefront. Academy Award winning
Mayam3D is the world's most powerhl3D animation and visual effects software
for the creation of advanced digital content. Maya provides a comprehensive
suite of tools for your 3D content creation work, ranging from modeling to
animation and rendering to name but a few.
If you have access to Maya, great. Otherwise, you can download the personal
learning edition of Maya (Maya PLE, its free) from the Alias web site. Refer to
the Appendix on how to download and install Maya PLE.
In this chapter, you will lean the following:
The basics of working with Maya
rn Using Maya to model polygonal and Nurbs surfaces
Using Maya to define material properties
rn Rendering a scene using ray tracing
8.7 Maya Basics
Critical to learning any software program is an initial understanding of some
basic concepts: The UI of the software, the hot keys and terminology used by the
software, etc. Maya is a tremendously powerful toolkit. We cannot imagine
teaching you all that it can do in one chapter. However, with a few basics in
place, it will be easy for you to unleash its power.
For clarity, we use a number of conventions.
When you are instructed to select a menu item we use the following
convention:
We use the same convention for selecting from the shelf or for any other
part of the Maya interface.
To start Maya
Do one of the following:
w Double-click the Maya icon on your desktop.
w (For Windows 2000 Professional) From the Windows Start menu, select
Programs > Alias>Maya (version number) > Maya (Complete, Unlimited or
Personal Learning Edition)
Let us first understand what we are seeing and how to use the interface. There
are a lot of items displayed in the Maya user interface. We won't look into all
these components right away. We will start with just the ones we need to create
our first few models. As we need to use more interface components, we will
introduce them to you.
The User lnterface
The user interface refers to everything that the Maya user sees and operates
within Maya. The menus, icons, scene views, windows, and panels comprise the
user interface.
Menu BY Slllw Unr She1 WWow Channel Box
Maya labels the x-, y-, and z-axes with a color scheme: red for x, green for y,
and blue for z. The color scheme is a visual aid used extensively in the modeling
of objects.
The Main Menu
Maya has support for modeling, animation, rendering, and various other CG
related activities. Each activity corresponds to a module within Maya. Modules
are a method for grouping related features and tools of the task you are
performing.
The Main Menu bar at the top displays the menu set for the module within
which you are currently working. You can switch between menu sets by
choosing the appropriate module from the menu selector. Let us create a
primitive 3D object from the Modeling menu set.
Choose the Modeling module.
Menu Seleclor
I
You've already learned the first item on the Status line: the Menu Selector
used to select between menu sets.
The second group of circled icons relates to the scene and is used to create,
open, and save your Maya scenes.
We will consider the other items shortly
Shelf
The Shelf is located directly below the Status line. The Maya Shelf is useful for
storing tools and items that you use frequently use. Some of the Shelf items pre-
configured for your use, tabbed into sections for convenience.
Select the cylinder icon located on the left end of the shelf by clicking on
it
Maya creates a cylinder primitive object as a Nurbs surface and places it at
the center of the scene. By our convention, from now on, the above command
will be specified as:
Select Surfaces>Cylinder from the Shelf.
(The same command can also be driven by the menu bar, as we saw earlier.)
In your scene, view the wire-frame outline of the spheres you created earlier.
They have changed color to navy blue, and the cylinder is displayed in a bright
green color. The cylinder is now the selected object (the sphere is no longer
selected). In Maya, when the object is displayed like this, we refer to it as being
selected or active.
Selection of an object or a component indicates to Maya that this particular
item is to be affected by the tool or action you will subsequently choose. As you
work with Maya, you will be selecting and deselecting items a lot.
To save your scene, choose the Save icon on the status line. You will be
prompted to enter a name for your 3D scene. Enter a name and save your world
into the Maya proprietary model file. You can always load it back up whenever
you want.
You should have the basic idea now to begin!
In this chapter, we will use Maya to model and compose our 3D world and
finally render the scene as a ray-traced image, as shown in ColorPlate 5.
188 8 2 MODEL
ING 3D OBJECTS
Table Mirror
The Table:
Let us start by creating the table. The table, as shown in Fig.8.3, can be thought
of as a rectangular cube (the base) with thinner but longer cubes as the feet. The
dimensions for this model are as follows:
15 by 2 by 7 units for the base.
1 by 6 by 1 unit for the legs.
Start Maya, if it is not running already.
Start a new scene by clicking the Create New Scene icon on the status line.
We will define the components of the table to be polygonal primitives, since we
don't need the complexity of a Nurbs surface. First, we define the base of the
table.
The Base
1. Choose the PolygonXube option from the shelf.
A unit cube will display in the perspective window. We want to scale this
cube to the dimensions specified. Scaling is easier to define in orthographic
windows, where we can see the exact x,y,z locations of the models.
Layout Shortcuts
I
The tool-box is a UI component located on the left hand side of the Maya
View Shortcut
user interface. The Quick Layout buttons on the lower half of the tool-box
provide a shortcut for you to select a different window panel or switch to another
layout.
2. From the tool-box, click the Four View layout shortcut.
The workspace changes to a four-view layout. The three new windows define
the three orthographic projection views along the x-, y- and z-axis (See Chapter 5).
&- -+I - -- , i ; ~ ~ % % + x , ? ~ ? ~
-. I :
Side V~ew '
They are referred to as the top view, side view and front view windows. These
windows are an invaluable tool in visualizing and designing the object from
different angles. The perspective view is located in the top right corner, and the
other views show the object from the top, front and side.
Each view has a grid defining the grid line markings along the axis. These
lines are like the lines of a ruler and are an invaluable tool in determining the size
of models. Let us set the markings so we can size our models within the same
units.
3. Choose Display>Grid> O from the menu bar
4. Set the Grid Lines Every option to be every 1 unit.
5. Set the Subdivisions options also to 1.
These steps will set up the grid lines to be drawn every 1 unit, with no
subdivisions in between. Every grid line corresponds to one unit in our world.
We will use these lines to size our models appropriately.
To size the table's width and length, we need to size it along the x-,y- and z-
axes. To do this, we will need to use the Scale transformation tool located in the
Tool Box.
The upper half of the Tool Box contains the tools for transforming objects
(selection, move, rotate, scale) within Maya. (When you move your mouse
cursor over any icon, the name of the tool appears next to the mouse cursor,
indicating what action you are going to commit).
Before you can transform an object, you must ensure it is selected. You can
select an object by clicking on it directly, or by dragging a rectangular bounding
box around some portion of the object to indicate what you want selected. To
deselect an object, you simply click somewhere off of the selected object.
6. If it isn't already selected, then click on the cube with the left mouse
button in order to select it.
7. Click on the Scale tool from the toolbox.
A scale manipulator icon appears over the primitive cylinder in the scene
view.
The Scale Tool Manipulator has handles that point in the direction of the
three axes. They are colored red, green, and blue, and control the direction of the
scale along the axes.
When you click a specific handle, it indicates that the move is constrained to
that particular axis direction.
8. In the top view window, drag the red x manipulator handle to scale the
object along the x-axis. As you drag the manipulator, you will see the outline
of the shape scaling up. Scale it enough so that it occupies 15 boxes along
The Legs
The legs of the table can be defined similar to the base.
1. Choose the Polygon>Cube option from the shelf.
2. Scale this new cube by Sx=l, Sy = 6 Sz = 1 or (1,6,1). (Remember to select
the new cube before applying the scaling transform.)
Now we need to move this leg to its correct position.
3. Select the Move Tool from the Toolbox. A move manipulator icon appears
over the cube.
The Move Tool Manipulator has handles that control the direction of the
movement along an axis.
When you click a specific handle, it indicates that the move is constrained to that
particular axis direction.
4. In the front view window, drag the green y manipulator handle to move
the leg downwards in the y direction. Move it downwards enough so that
192 8 2 MODELING
3D OBJECTS
We need three more legs. We can create them by duplicating the leg we already
created. The shortcut to duplicating an object is the hot key: Ctrl-D (i.e., pressing
the Ctrl and the D buttons of the keyboard together)
6. Select the leg. Hit Ctrl-D. This will create a copy of the selected object.
7. Choose the Move transform and move the newly created leg to the left
comer. Repeat the process for the back two legs.
When you first start Maya, the default selection mode is set to Objects. This
means we select individually defined components. Now that we have grouped
our components under a hierarchy, we want to select the entire hierarchy. To do
this
6. Change the selection mode to Hierarchy by clicking on the Hierarchy
selection mode on the Status line.
7. Now if you click on any component of the table, the entire hierarchy o f t
he table model is picked.
8. Save your work by choosing the Save Scene icon from the status line.
9. Save your model in a file called Table. Maya saves the model in its
proprietary format.
Whew. One model down, four to go. By now you must have started to
appreciate the power of modeling software such as Maya. But the h n is yet to
begin!
The Mirror
The steps to create the mirror are similar to those we followed when modeling
the table. The mirror can be modeled using two cubes with the dimensions
shown in Fig.8.3. The reflective (mirror) should be slightly in front of the frame.
1. If Maya is not running, start it again.
2. Start a new scene by selecting the Create New Scene icon on the status
line.
3. Create a polygonal cube.
We wish to scale the cube so that it has the dimensions (15,10,1). You can use
the scale manipulator we saw when creating the table rr we can use the Channels
editor. The Channels editor shows up on the right-hand side of the Maya
interface and displays transformation information for the currently selected
object.
If you don't see the Channel editor click on the ShowIHide editor for the
Channel box on the status line.
bns
Snap ~ o d a ShOwMido Mar %om
1=.---
& p . ~Show
----
Help
scene: including the windows and lights being used. It is very useful for
grouping, setting names for models, choosing models in the scene, etc.
Your two cubes will show up as pCubel and pCube2 (p is for polygonal), or
something similar.
11. You can rename the newly created parent as Mirror by double clicking
on the name of the group and entering the desired name.
12. Save your scene in a file called Mirror.
If you find you need to edit the curve follow the steps below
I. Select the curve.
2. Click down on thc right mousc button until a ncw window called the
Marking Menu pops up.
The marking menu is used for context- related actions. The way to use it is to
click the right mouse down till the menu shows up, and then drag the mouse to
select the item desired.
3. Do not release the mouse. Drag the mouse to the menu item marked
Control vertices to highlight this item.
4. Release the mouse button. You will now be able to see the CVs of
your curve.
5. If you select the Move Tool and then click on a CV, you will see the
move manipulator. Use the manipulator to move the CV around as
desired.
6. Repeat step 5 until all thc CVs are positioned appropriately.
I I . Using the marking menu, select the Object Mode item. The CVs
disappear.
12. Switch back to the four-window layout by clicking on the Four View
layout icon in the toolbox.
13. Select the curve by clicking on it.
14. To revolve this curve, select Surfaces>Revolve from the menu bar. You
will see the revolved surface appear in the perspective window.
15. Click in the perspective window.
16. You can zoom in by pressing the Alt key and dragging the mousc with
the right-mouse button held down. Dragging the left-hand mouse button
with thc Alt key held down will enable you to view thc glass from
different angles. Pressing the Alt key and dragging with the middle
mouse button held down will cnablc you to move the sccnc up and down.
17. Click in the perspective view, press the keyboard key 5 (a shortcut for
Shading>Smooth Shade All option in the menu bar in the perspective window).
This displays the martini glass as a shaded surface. Spin the view around so you
can view your model from all angles. Select the keyboard key 4 to display the
model in wire-frame mode again.
18. If you are not satisfied with your model, choose Display>Outliner. The
outliner window will enable you to select just the Nurbs curve you had
first created.
19. Follow steps I to 6 to modify this curve.
20. Your surface of revolution will change its shape as you move the CVs
around.
21. Save your scene into a file called MartiniGlass.
Stirrer
Do not create a new scene. We will create a stirrer with the martini glass as our
guide.
1. Select the martini glass (and the curve used to create it).
2. Select Display>Object Display>Template from the menu bar. This will
template the martini glass. A templated model still displays the wire-frame hull
of the mode, but you will not be able to easily select it.
We wish to create the stirrer as a cylinder with radius=0.05 and height = 2. You
can use the Polygon>Cylinder option from the Shelf as w had done earlier.
Rotate
manipulator
The Rotate Tool Manipulator consists of three rings (handles), plus a virtual
sphere enclosed by the rings. The handles are colored red, green, and blue based
on their fimction related to the x-, y- and z- axes and control the direction of the
rotation around an axis.
14. In the front-view window, drag the blue Z manipulator ring to rotate the
stirrer so it is aligned with the edge of the glass.
You are rotating the cylinder around its z-axis. What is the pivot point?
Alternately, you can also set the RotateZ (Rz) value for the group to be about 6.3
in the Channels editor.
15. You may have to translate the stirrer further to place it within the glass.
16. To define the drink inside the glass, create a polygonal cone primitive
with a radius of 1 unit and a height of 1 unit also.
17. Rotate the cone by setting its RotateZ = 180
18. Position it within the glass by settings its TranslateY = 0.7.
19. Click Edit>Select All from the menu bar.
20. Click Display>ObjectDisplay>Untemplate from the menu bar to un-
template the glass.
21. View your creation in the perspective window by hitting 5 to see the
objects as solid.
22. Choose Window > Outliner.
The Outliner appears and displays a list of the items in the scene.
23. In the Outliner, select the curve you revolved and delete it, so you will
not accidentally modify it later on.
24. Group all the objects under one parent.
25. Rename the group to martiniglass.
26. Save your work back to the file called MartiniGlass.
Lamp 1
Lamp1 has a cylindrical stand with dimensions as shown in Fig.8.3.
We shall start by defining the materials of the two lamps first, since these are
the simplest. We will then define the material of the martini glass which needs
transparency defined for the glass surface; the mirror, which needs reflectivity
settings; and finally the table, which needs a texture setting for its wood.
By default, Maya assigns a default material called lambert to all models
created. This default material is grey in color and has diffuse-only reflection
defined. To assign new materials to objects, we need to create new material
attributes.
Lamp 1
1. In Maya, click on the Open a scene icon from the Status line.
2. Type in the name of the model file where you saved Lampl. This will
open up your Lampl model.
3. Set the selection mode to Object and select only the lamp stand.
The Lamp Stand
Let us first define the material for lamp stand. We want the stand to be a
reflective metallic material so we will need to create a new material for this
object.
1. Right-click the stand to select it.
2. Select Rendering>Assign a New Phong Material icon from the shelf.
This option will create a new material and assign it to the lamp stand. Phong
shaders includes all three components of the illumination model: ambient,
diffuse, and specular components. A Phong material will give a surface a shiny
look.
The Attributes editor will appear on the right hand side of the Maya UI. The
editor will display a sphere rendered with the current material attributes. If you
don't see the Attributes editor, click the ShowIHide attributes editors from the
status line (it is to the left of the Channels editor button).
Below the sphere on the Attributes editor, is the menu to modify the attribute
settings of this material.
3. Click in the text box that defines the name of the material and change it
to Chrome so that we can identify this material for use later on in the
project. The name of the material is at the topmost text box of the editor.
-.- -
L
i-
-- , . .
c-i-
-7,
-ma..---. t
-I.-_
--• ?;
t
I- I Y- l -1. i
4. In the Common Material Attributes section click inside the color box to
the right of Color. This will open the Color Chooser window.
5. The Color Chooser window displays a color wheel. You can click inside
the color wheel (hexagon) and drag the mouse pointer to change the
color of your material.
6. You can also set the color by specifying the RGB components. Change
the slider display from HSV to RGB as shown, and set the RGB values
as R=0.96, G=0.96, B=0.5-a yellowish color for our chrome stand.
7. Click Accept to choose the new color and close the Color Chooser.
8. You can change the intensity of the diffuse (and ambient) reflectivity of
your material by dragging the Diffuse Color (or Ambient Color) slider
to its right. Do so to increase the color brightness.
9. You can change the specular settings of your material by using the
Specular Shading section in the attributes editor. The Specular Color
option changes the specular reflectivity of the material (and its
intensity), and the Cosine Power slider changes the size of the shiny
highlights.
10. Ignore all the other attributes for now. Click in the Perspective window.
11. Select the IPR Render Current Frame icon from the Shelf.
This will render an image of the currently selected window. Since you
clicked in the perspective window earlier, this will render an image of your lamp
as seen in the perspective window.
12. You can change the perspective camera (by hitting Alt and Mouse
right/middle/left) to view the lamp from different directions. Hitting the
IPR button will render the lamp from the new viewpoint.
13. Change your material until you are satisfied with your render.
14. If you lose the material editor, choose your lamp stand again and choose
the Show shading group attribute editor icon from the Shelf. You can
then tab to the Chrome material in the Attributes editor
The lampshade
The lampshade is made of cloth and has a diffuse material. We will create a new
Lambert material for the shade. Lambert shaders have only ambient and diffuse
components and hence do not look very shiny-Perfect for a cloth.
1. Select the Rendering>Assign a new Lambert material icon from the Shelf.
2. Set the name of this material to be shade, and set its color to be R=0.9,
G=0.5 B=O-an orangish tinge
When you see a lamp shade in real life, it seems to glow from the light within it.
We can achieve this effect by giving the shade a bit of emissive color.
3. To do this, drag the Incandescence slider, under the Common Materials
Attributes section, to the right. The Incandescence of a material defines
the emissive nature of the material.
4. Render till you are pleased with the results!
5 . All done with lamp1 ! Save your scene back to avoid losing your work.
1. Select the Open a new scene option and open the file Lamp2.
2. Set the selection mode to be Object mode.
3. Select the stand and assign a new Phong material to it.
4. Set the name of the material to be metal.
We shall assign a metallic-looking material to the lamp and the lamp shades.
5. Assign this material to have a whitish color. RGB = (0.8, 0.8, 0.8).
6. Under the Specular shading options, set the specular color to be the
same color.
If you have looked carefully at a metallic object such as a spoon, you may
have noticed that you can see reflections in it. Metallic objects reflect their
environment. Maya enables materials to reflect their environment by the value of
the Reflectivity attribute. The slider to control this attribute ranges from 0 (no
reflections) to 1 (clear reflections). The default value is 0.5. Reflectivity values
for common surface materials are car paint (0.4), glass (0.7), mirror (I), metal
(0.8).
7. Set the reflectivity value (under Specular Shading options) to have a
value of 0.8.
8. You will not see the reflections in the IPR render. When we ray-trace
this object, reflections in the object will give it its true metallic look.
9. Pick the cones of the shade and set them to have the metal attributes.
You can do this by selecting MateriaWAssign Existing MateriaWMetal from
the marking menu.
10. Pick the cylindrical caps of the shade.
We want the lamp shade to look like it's glowing-as if the light is coming out of
this object.
11. Select Rendering>Assign a new lambert material icon from the shelf.
12. Set the name of this material to be shade2, and set its color to be R=0.9,
G=0.9 B=O-a yellowish tinge.
13. Drag the Incandescence slider to the right. This component will change
the emissive color of the material.
14. Render.
15. Redefine the materials until you are pleased with the results!
16. Save your scene.
Martini Glass
1. Load up the martini glass.
2. Pick the revolved glass object.
3. We need to assign this object a glass material. Glass is transparent, but
it still displays specular highlights and some reflectivity.
4. Assign the glass a new Phong shader and call this shader, glass.
5. Leave the color of the glass as is.
6. Glass is transparent: we can see through it. To set the transparency of
the material, slide the Transparency slider. You can slide it to the right
to get a nice transparent glassy look- but don't drag it all the way to the
right, or your object will become invisible!
7. Under the Specular Shading options, we set the Reflectivity of the glass
to be 0.2. This will make the glass show very faint reflections.
The cone inside the glass is made of water. Water is also transparent, but it is
usually perceived as blue in color.
8. Pick the cone inside the glass. We will make this object be a water
material.
9. Assign it a Phong shader.
10. Set the color of the water to a bright blue. RGB= (0,0.9,1)
11. Slide the Transparency slider to get a nice transparent water look.
12. Again, we made the water nonreflective, although you can define a
small amount of reflectivity to it if you like.
13. Set the stirrer to also have the shader glass, by selecting
Materials>Assign Existing Material>glass from the marking menu.
14. If you lose your material, remember. You can always pick the model
and click on the Show shading group attribute editor icon from the
Shelf.
15. Create a new green-colored Lambert shader, with RGB=(0,0.5,0) for
the olive.
16. Save your scene to avoid losing your changes.
Mirror
1. Load the mirror.
2. Select the outside cube-the frame of the mirror.
3. Assign it a new lambert shader.
4. Set the color of the shader to be magenta: RGB = (0.8, 0,0.2)
5. The mirror itself has a black color, and is completely reflective.,
6. Select the mirror.
7. Assign a new Blinn shader to it, and call the material mirror.
8. Set the color of the mirror shader to be black and the diffuse color also
to be black. The mirror itself dose not have any color. It merely reflects
its surroundings.
9. In the Specular Shading section, set the specular color of the mirror to
be 1 by sliding the slider all the way to the right.
10. Set the reflectivity of the mirror to be 1. The mirror reflects everything!
11. Save your scene
Table
1. Load the table model.
2. Pick the base of the table.
3. Assign it a new Phong material. Name the shader wood.
4. Click the checker icon next to the Color slider. This is a quick way of
applying a texture.
A window called the Create Render Node window pops up and displays a list of
the textures that you can apply. The texture names and their icons help you to
decide which texture is appropriate for the effect you want.
iGx-G- :----.
- -
6. A green box indicating the position of the texture cube will be displayed
in the windows.
7 . The various parameters to define the values for this texture will appear
in the attributes menu.
8. Under Wood Attributes, click on the filler color. In the color selector, set
the RGB color of the Filler to be R=0.5, G= 0.26, B = 0.0 to get a rich
brown color, which is what oak wood normally looks like.
9. Under Effects, turn on the Wrap radio button, so the texture will repeat
itself. .
10. Render the table.
You will notice that the wood grain is closely packed. We need to scale the texture
box up to scale out the wood grain. Also, the texture needs to be rotated so the texture
cube is mapped correctly. We will need to rotate the texture box to achieve this.
11. Click on the place3DTexturel tab in the Attributes menu.
This tab will enable you to position and scale the texture appropriately.
Essentially, you will be applying transforms in the texture space.
12. Set RotateX =-90 and RotateY = 90
13. Set ScaleX =2, ScaleY= 4 and ScaleZ = 1.
You will see the green texture box now engulfing more of the table.
14. Render.
15. You should see the tabletop rendered with a nicely defined wood
texture.
16. Assign the wood texture to each of the legs by selecting each one and
choosing MateriaWAssign Existing Materials> Wood from the
Marking menu.
In the Attributes menu, you can navigate to the wood shader by
selecting any object in the scene which has the wood shader defined.
You can navigate fiom the wood shader to the wood texture by clicking
the icon next to the Color slider.
When you render the image, the legs will not have the texture mapped onto it.
This is because the texture bounding box does not engulf the legs.
17. Scale up the wood texture placement by setting ScaleZ = 7.
18. Render.
19. Save your scene.
When you open a scene, Maya lights it with default lighting provided by an
imaginary light fixture.
Once you define your own lights, Maya will render using your defined lights
instead. Maya supports ambient light, directional lights (like sunlight), spotlights
(useful for cone lights), and point lights, to name a few.
Let us define lighting for the current scene.
Ambient Lighting
First we define some ambient lighting so that nothing in our scene is rendered
black.
1. Choose the Rendering>Ambient Light icon from the Shelf.
2. The attributes menu for the newly created light will show up.
3. Under the Ambient Light Attributes section, set the intensity ofthe light to be 0.2.
16. Usually, shadows are never stark black. They are some shade of grey,
depending on the lighting. To achieve this effect, we also modified the
Shadow Color attribute to be a dark grey.
17. Create a similar spotlight for the shade on the other side. You will have
to position it appropriately.
18. Set this light's cone angle to be 60, penumbra angle to be 10 for a soft
light on the wall, intensity to be 0.8, and color to be a light yellow. You
should be able to see a cone of light on the wall when you render the
image.
Render the scene - wow! Doesn't it look amazing? And we still haven't gotten
to ray tracing. This will be the final step, when we will see reflections;
refractions and shadows all come together to enhance the visual realism of our
image.
This will bring up a window allowing you to set the rendering options.
2. Set the Render Using option to be Maya Software.
3. Under Resolution, set the resolution to 640 x 480 or higher if you wish.
This sets the rendered image to the specified size.
4. Click on the Maya software tab.
5 . Under the Anti-aliasing Quality options, set Quality to be Production
Quality-this will be a quality render.
Summary
In this chapter, we have learned how to use Maya to model and render a 3D
scene. Could you visualize what code was running in the background to display
the models and images? By now, you know enough to actually write a small
Maya package yourself!
The next section will deal with how to make our scene come to life by adding
animation. We shall also see how to add more pizzazz to our scene with
additional lighting and viewing techniques.
Section 3
Making Them Move
In the last few sections, we saw how to model objects and render them to create
photo-realistic images. Our next task is to give life to our creations by making
them move: animation!
When you see a cartoon on TV, you see characters in motion. This motion is
just an illusion: the strip itself is actually composed of a sequence of still images
(or frames) which when played back in rapid succession gives the illusion of
motion. The most popular way to create animation is by generating a number of
successive frames differing only slightly from one another. However, just
making the characters move is not enough to make an animation look believable.
Walt Disney himself put forward the seven principles of animation that add zest
and realism to the animation.
In Chapter 9, we shall look into the techniques and principles used to create
traditional (hand drawn) 2D computer animation and how they can be adapted in
a 3D world. Chapter 10 will walk you through the creation of an animated 3D
game. In Chapter 11, we shall use Maya and work through the process that
traditional production houses use to create our own animated production.
To animate means to bring to life. In the last few chapters, we saw how we can
use the principles of Computer Graphics to draw and render 3D worlds. Our next
task is to breathe life into our models.
When you see a movie on television or in the theater, you see characters in
motion. This motion is usually smooth and continuous. The actual tape
containing the movie, however, consists of a sequence of images, or what are
known asfiames. These frames, when flashed in rapid succession on the screen,
cause the illusion of motion. Fig.9.1 shows such a sequence of frames in which
a smiley face changes into a frowning one.
Once the key frames are drawn, the next step is to draw the intermediate
frames. The number of intermediate frames, or in-betweens as they are called,
depends on the length of the animated sequence desired (keeping in mind the fps
count that needs to be achieved). This step of drawing in-betweens is also called
tweening.
If we were going to create a video of the face smiley animation, we would
need to create 30 fps. For a short five minute animation, we would need (30 fps)
x (60 seconds) x (5 minutes) = 9000 frames to be drawn! Now you know why
Disney hires so many animators to work on a single movie.
A technique that helps tremendously in the process of creating animations, is
called eel animation. When we create an animation using this method, each
character is drawn on a separate piece of transparent paper. A background is also
drawn on a separate piece of opaque paper. Then, when it is time to shoot the
animation, the different characters are overlaid on top of the background. Each
frame can reuse the same elements and only modify the ones that change from
one frame to the next. This method also saves time since the artists do not have
to draw in entire frames-just the parts that need to change, such as individual
characters. Sometimes, even separate parts of a character's body are placed on
separate pieces of transparency paper.
Traditional animation is still a very time-consuming and labor-intensive
process. Additionally, once the frames are drawn, changing any parts of the story
requires a complete reworking of the drawings.
How can we make animation less laborious and more flexible? The answer
is found in the use of computers.
Computers are used by traditional animators to help generate in-betweens.
The animators sketch the key frames, which are then scanned into the computer
as line strokes. Many software programs are also available that enable drawing
directly on the computer. The computer uses the line strokes of the key frames
to calculate the in-betweens. Using sophisticated algorithms and equations, the
in-betweening can be made to look very natural and smooth.
The use of computers is not, however, limited to this minor role. Since the
advent of 3D Computer Graphics, computers are used in a big way to generate
the entire animation sequence in a simulated 3D world. The sequence can be a
stand-alone film, like Toy Story or The Incredibles, or can be composited into a
real action film, as in Lord of the Rings or Terminator-2.
Let us learn some more about some basic interpolation methods and how we
can use them to define an animation.
Linear Interpolation
The simplest type of interpolation is linear interpolation. In linear interpolation,
the values of a given property is estimated between two known values on a
straight-line basis (hence the term linear). In simpler terms, linear interpolation
takes the sum of the property values in the two key frames and divides by the
number of frames needed to provide as many equally spaced in-between frames
as needed. For example, consider a model P, with a position along the y-axis of
Yi, at the initial key frame Kin.At the final key frame, Kfin,they position has
moved to Yfin.Now, say we want only one in-between frame f. This frame would
be midway between the two key frames, and P would be located midway
between the positions Yi, and Yfinas shown in Fig.9.3.
?
Fig.9.4: Two inbetween frames
Mathematically,
Yfl) =Yin + (YJin - YiJ / 3
In total, we would have four frames: Kin, fl, f2, and Kfin-enough to make
an animation that lasts for 116th of a second (assuming 24 frames per second).
Animations are usually represented in terms of timelines. The animation
timeline defines the frames of the animation along an axis. The property values
for the active object at these frames can be depicted along this axis either
visually or just as values. For our example, we could define the following
animation timelines:
Kin
Kin
Fig.9.5: Animation timeline
If we were to extend our discussion to a total of N frames (including the
initial and final key frames), we would have N-2 in-between frames. The
position Y at any frameJ can be can be calculated by the equation
05fl.N-I
At f r a m e p 0, we attain Y(0) = Yin: the initial key frame.
At frame f = N- 1, Y(N- 1) = Yfin: the final key frame.
The N-2 in-between frames can be calculated by varying f from 1 to (N-2).
As we vary f , we linearly interpolate the position of P from Yin to Yfin.
Do you recognize this equation? Yes-it's the parametric equation of a line!
The parameter we use is the frame number.
We can define similar equations for interpolating the position of P along the
x- and z-axis:
x f l =xin + f " ( X j , - xi,)/ (N - I)
zfl=zin +f"(Zjn - ZiJ (N- 1)
OLf(N-I
This set of equation enables us to linearly interpolate the position of model P
between two key frames. We can apply the same equations to interpolate rotation,
scale, color, etc of the object. What we are doing is calculating the transformation
at each in-between frame using a linear interpolation equation. The code to
calculate the intermediate value is as shown below and can be found in the file
linearinterpolation.cpp,under the installed directory for our sample code:
Example Time
Let us take the example of a bouncing ball to illustrate how linear interpolation
works. We will initially move the ball only along the y-axis to grasp the
concepts. Then we will expand the motion along the x-axis as well.
LJ A And C
Timeline
Fig.9.6 : Key frames and graph of a bouncing ball
As shown in Fig.9.6, a ball is initially at position A=(O,Ya,O). It goes up to
position B=(O, Yb,O) and the falls back to Position C=(O, Ya,O). The motion of
the ball can be drawn as a graph of its position in Y against the animation
timeline, also shown in Fig.9.6.
The position of the ball at A, B and C define our key frames. Let us say, we
want our animation to be 20 frames long. Further, let the three key frames be at
frames 1,10, and 20 along the animation timeline. To define the linear
interpolation between positions A and B, we go through 10 frames from 1 to 10.
The interpolated values of Y for any frame f is defined as
Yfl = Y,+f-I) *(Yb-YJ(I0-I)
1sf 5 1 0
In this same sequence, positions for frames between 10 and 20 need to be linearly
interpolated between positions B and C. These positions can be expressed as
Yfl = Yb+f-l 0) *&- Yd/9
10< f 1 2 0
Using the two equations, we are able to define the position of the ball at any
intermediate h m e in the animation.To generalize the code to calculate these equations,
assume we stored the positions of the key fi-ames in an array called KeyFramePositions[].
In our example, KeyFramePositions = {0,10,20}.We store the property values in an array
called KeyFrameValues[], which in this case = {Ya, Yb, Y, }. Then we can define a
h c t i o n that can calculate the intermediate value at any given h e as
--
N KeyFramePositions[i + 11-KeyFramePositions[il;
InitF KeyFramePositions[il;
11 interpolate between KeyFramePositions[il and KeyFramePositions[i+ 11
-
value InterpolatedValue(KeyFrameValues[i], KeyFrameValues[i+ I],N, If-InitFI);
retum value;
The code can also be found in the file provided: 1inearinterpolation.cpp.
In Example9-1, we define a ball bouncing up and down. Its position at
KeyFrame A is Ya = 0; at B, Yb = 25; and at C, Yc = 0 again. We define
-
-
GLFloat "KeyFramePositions {I,I0,20).
Glfloat *KeyFrameValues {0,10,0)
- -
int NumberofKeyFrames 3;
int MAXFRAMES 20;
If we let the glut sphere represent our ball, then the code for this animation looks
as follows:
-
ypos EvaluateLinearAt(KeyFrameValues,KeyFramePositions,
NumberofKeyFrames, currentFme1;
glloadldentity (I;
gluL00kAt(0.,0.,13,0,0,-100,0.,1 .,O.I;
glTranslatef(O.,ypos,O.I;
glutWireSphere(.5,10,10I;
you need to make to the Example9-1 is to define the kcy-frame arrays as:
-
GLfloat KeyFrameValues[91 {0,10,20,30,40,50,60,70);
-
int NumberofKeyFrames 9;
-
GLint KeyFramePositions[91 {0,10,0,7,0,4,0,1,0);
-
int MAXFRAMES 71;
Voila! You will see a ball bouncing.
Let us now add in some motion along the x-axis, so that the ball appears to have
been thrown by someone, and is slowly bouncing to rest.
In order to accomplish this, we define a set of key frames for motion along the
x-axis as
KeyFrameXPosValues[O]
KeyFrameXPosValues[ll
-- 0;
3.5;
KeyFrameXPosValues[2] - 6;
KeyFrarneXPosValues[3]
KeyFrameXPosValues[4]
-- 8;
9.5;
KeyFrameXPosValues[5] - 11;
KeyFrameXPosValues[61
KeyFrameXPosValues[7]
-- 12;
12.5;
KeyFrameXPosValues[8] - 13;
We now interpolate the ball's x position as well as they position by calling the
interpolation function
As usehl as these equations are in modeling, they are even more usehl in
animation! Instead of interpolating vertex points of a model, we can interpolate
key frame positions of an animation using the very same equations. The
parametric cubic equation set can be replaced to use the frame, f, as the
interpolating parameter:
X f l = a d 3 + bJ2 + cJ+ dx
Yfl = a d 3 + bJ2 + cJ+ dx
Zfl = a d 3 + bJ2 + cJ+ dx
f = Cf-mi@/(N-1)
Initf_<f,<N=>O5f_<1
The coeficients define the type of cubic spline being used. We covered
bicubic splines in Chapter 7. Bicubic splines are great for modeling, but they can
be cumbersome to use, since they only approximate the two interior points. In
this chapter, we shall use coefficients that specify a derivative of a hermite cubic
spline called the Cardinal cubic spline. The advantage of this spline is that the
curve interpolates all the points specified. We need at least four key frames in
order to define a cubic interpolation. In the file, cubicinterpolation.cpp, we
define a function to evaluate a Hermite cubic interpolation
-
GLfloat tension 0.3;
GLfloat CardinalValue(GLf1oat KFValuernl, GLfloat KFValuel, GLfloat KFValue2 GLfloat KFValuep2, int
N, int f1
{
GLfloat F - 1.O*fl(N-11;
GLfloat tangent1 - I1.-tension1*(KFValue2-KFValuern1112.;
GLfloat tangent2 - I1.-tensionl*lKFValuep2-KFValuel112.;
Gngent vector
1 10 20 30 40 50 60 70
Time linelframes
Fig.9.8: Hermite Interpolation Timeline
You can see how the ball's motion slows down at the control points.
In cubicinterpolation.ccp, we also define a function:
This function figures out which four control points to send in to the
Cardinalvalue function. It also appends a copy of the first and last points to our
keyFrameValues array to interpolate the first and last points correctly. In
Example9-3, we modifL our ball animation to now use the cubic interpolation:
Watch the ball bounce. Does the animation look smoother? We achieved the
desired slow-down of the ball at the top peaks. However, do we really want the
same effect at the bottom? The ball should actually speed up at the bottom! Try
changing the value of the tension parameter to see how the motion is affected at
the endpoints. Most animation software employs equations that let the user
interactively tweak the interpolation graph at every control point in order to
achieve desired behavior.
There are cases when cubic interpolation does not work too well. Consider a
situation where, in key frame D, we want the ball to stay on the ground as shown
in Fig.9.9. The graph shows a wiggle between points C, D and E that makes the
graph go below the 0 level we defined for the ball. If we had the ball animating
TimelineIFrames
Fig.9.9: A wiggle below the ground
on top of a ground plane, then the ball would go underneath it! We would need to
add in extra control points to avoid this behavior. Try this with your ball animation
by changing the KeyFrameValues array and see what happens. Try it for the linear
interpolation case as well. Unlike linear interpolation, cubic interpolation does not
retain the values of adjacent key frames when these are equal.
It is hard to achieve accurate realism using only one interpolation technique.
In most cases, a combination of different schemse is needed to get good results.
Extensive ongoing research constantly delivers better ways to make realistic
interpolations possible. In any production, the animator has a big role in
understanding how interpolation works and how to modify the interpolation
graphs to achieve desired affects.
Animating Snowy
Let us use our interpolation equations to create a slightly more complicated
animation. Recollect our snowman model from Chapter 5. We shall use it to
create an animation of a snowman bouncing on the ground.
In Example9-4, we reuse the snowman model from Chapter 5. We changed
the snowman model code to comment out transforms that we did not need - for
sake of speedier display. Again for speed considerations, we shall preview our
snowman animation in wire frame mode.
In this example, we will only animate the root level component of the
snowman - the null node. Recall that the null node is the parent of all the
components of the snowman. Its pivot point is at the base of the snowman.
Recall that this node (as well as each component of the snowman) had a
transform array applied to it. These transforms were specified in an array of size
9 storing the (Tx,Ty,Tz,Sx,Sy,Sz,Rx,Ry,Rz) values:
-
GLfloat snomanXforms[91 {0.,0.,0.,0.,0.,0.,1.,1.,1.);
-
GLfloat botXforms[9] {0.,0.,0.,0.,0.,0.,1.,1.,1.);
The array to hold the key frame positions and number of key frames defined
for the snowman are defined as follows:
GLint smKeyFramePositions[101;
//This array holds the actual positions of the key frames defined for the snowman
GLint smNumKeyFrames;
//The number of key frames that the snowman has
We define a utility function to actually initialize the key frame values for the
snowman.
This function figures out the appropriate key frames to interpolate between
and then calls the linear interpolation function to evaluate the transform for the
current frame. This transform is then stored in the parameter array: xforms. Let us
bounce Snowy up and down on a snowy ground. We define our world with it's
extents being about -2 to 15 units along the x- and 0 to 12 unit along the y-axis.
We define a snowy ground plane as a solid cube that is grayish in color.
void draw-Ground(){
glPolygonModelGL-FRONT, GL-FILL);
glPolygonModelGL-BACK, GL-FILL);
glColor3f(0.8,0.8,0.8);
glPushMatrix0;
glTranslateflO.,-5.5J.l;
glScalef(25.,10.,10.);
glutSolidCubeI1.I;
glPopMatrix0;
At frame 30, Snowy rises back up again, and at frame 45 he lands back down-
but oh no! This time he has not landed back on the ground!
A timer function periodically updates the current frame number and calls the
Display function. The Display function merely calls the EvaluateXformsAt to
evaluate the transforms of the snowman for the current frame and calls the
snowman drawing code with these transforms.
void Display(void)
{
GLfloat ypos, xpos;
glutSwapBuffers0;
In frame 40, Snowy lands (but not on the ground); we clamp his motion on
frame 45 so he won't move any farther for the rest of the animation:
View your animation. You can add in surface properties to the snowman and
light up your scene to see a solid bouncing snowman.
The code can be found under Example9-4 in files, Example9-4.cpp7
Smowman.cpp and Sn0wman.h. The animation looks good; but it can still do
with some zing!
the ground to enhance the feeling of speed. Hence, squash and stretch effects
help to create illusions of speed and rigidity. Its one of the most important
principle used in animation to simulate realism. Human facial animation also
use the concepts of squash and stretch extensively to show the flexibility of the
skin and muscle and also to show the relationship between parts of the face. We
will use the principles of squash and stretch to add some life to our Snowy
animation.
We will be using the scaling transforms to add squash and stretch to Snowy.
At frame 10, Snowy is just about to land on the ground. Let us stretch him up at
this frame to enhance the feeling of him speeding up in motion before impact
with the ground. To do this, set Sy= 1.2. Since we wish to retain volume, we will
squash him down in x- by setting SX= 0.8:
SetKeyFrame(1O,smKeyFrameValues, smKeyFramePositions,
&smNumOfKeyFrames, 4.,0.,0., 0.,40.,20.,0.8,1.2,1.1;
At frame 13, Snowy has made maximum impact with the ground. He should be squashed to
- -
depict this force. Set Sx 1.2 and Sy 0.8
Finally, at frame 16, Snowy is ready to launch back into the air. Set his Sy =
1.2 and Sx = 0.8 to give the illusion of him stretching back up:
At frame 40, we will make Snowy stretch as if he is about to land on the ground
(even though he really isn't!)
View your animation. Does the animation start looking more realistic
already? And we still have more to go!
Staging
Another important concept in animation is staging. Staging is the presentation of
an idea so that it is completely and unmistakably clear. A personality is staged so
that it is recognizable; an expression or movement is brought into focus so that
it can be seen. It is important to focus the audience on only one idea at a time. If
a lot of action is happening on the screen at once, the eye does not know where
to look, and the main idea will be upstaged. The camera placement is very
important to make sure that the viewer's eye is led exactly to where it needs to
be at the right moment. Usually, the center of the screen is where the viewer's
eyes are focused. Lighting techniques also help in staging.
Let us apply staging to our Snowy animation. We shall make Snowy
complete his second bounce only to find himself off the plateau and standing on
thin air! To stage this part of the animation, we want to move the camera to zoom
in on Snowy's current location. Such a swing in the camera is called a cut.
A cut splits a sequence of animation into distinctive shots. The transition
from shot to shot can be a direct cut, which we shall use in this example. It could
also be a smooth transition, which is something we will look into in a later
chapter. Usually, the last few frames from the first shot are repeated in the second
shot to maintain continuity between the shots. Let us implement these concepts
to refine our Snowy animation. First, we increase the total number of frames in
our animation:
if IcurrentFrame -- 41 I { 11 cut
glLoadldentity0;
gl~L00kAt(l6,5.,12, O,O,-100,0.,1 .,O.);
1
else if IcurrentFrarne -- 0) { I1 orginal location of camera
glLoadldentity0;
gluLookAt(8,7.,17, Or&-100,0.,1 .,O.);
1
How would you implement the camera repositioning in the case of a smooth
transition between the shots? Snowy himself needs more key frames defined. We had to
increase the size of all the key fi-ame arrays to accommodate for these new key fi-ames.
You can go ahead and view your animation or wait until we add in more features.
(Yes, we agree: the cut looks jerky! We will show you how cuts are made smooth
in the next chapter)
Anticipation
Anticipation involves preparing the objects for the event that will occur in the
later frames of the animation sequence. If there is no anticipation of a scene, the
scene may look rather abrupt and unnatural. The anticipation principle makes the
viewer aware of what to expect in the coming scene and also makes the viewer
curious about the coming action. Anticipation is particularly necessary if the
event in a scene is going to occur very quickly and it is crucial that the viewer
grasp what is happening in the scene.
Continuing our Snowy animation, we already have the stage set up for the
audience: they have seen Snowy land on thin air. To create more anticipation, we
shall make Snowy look down to give an impression that he is trying to fig. out
what he is landing on. This would draw a viewer's attention to the fact that good
old Snowy is going to have a free fall from this frame onwards.
The key frames for the head from frames 1 to 45 need to be clamped to their
identity values:
From frames 47 to frame 55, Snowy looks down to see why he hasn't squashed:
Timing
Timing refers to the speed of an action. It gives meaning to the movement in an
animation. Proper timing is crucial to make ideas readable. Timing can define the
weight of an object: a heaver object is slower to pick up and lose speed than a
lighter one. Timing can also convey the emotional state of a character: a fast
move can convey the sense of shock, fear, apprehension, and nervousness, while
a slow move can convey lethargy or excess weight.
In the Snowy animation, we let Snowy look down lazily, using 8 frames to
look down (from frame 47 to frame 55). However, when he realizes his coming
plight (free fall) we want him to look up in shock. We shall make him look up in
only two frames to convey this sense. We also define his eyes to enlarge to
exaggerate the sense of shock:
SetKeyFramel55,sheadKeyFrameValues, sheadKeyFramePosiiions,
&sheadNumOfKeyFrames, 0.,0.,0.,40.,-20.,0.,1 .,I.,I.I;
SetKeyFrame(57,sheadKeyFrameValues, sheadKeyFramePosiiions,
&sheadNumOfKeyFrames, 0.,0.,0., 0.,-20.,0.,1 .,I.,I.I;
Almost there-one last trick and we will be able to view a very realistic animation.
Secondary Action
A secondary action is an action that results directly from another action.
Secondary actions are important in heightening interest and adding a realistic
complexity to the animation.
For Snowy, a secondary action would be his hands moving up and down as
he bounces up and down. This motion of his hands would help him maintain his
balance and also add some realism to his bouncing. His hands would move down
when he is landing and raise up as he bounces up. When Snowy finally falls, his
hands swing upwards to convey the sense of free fall that he is in. The key frames
to rotate the hand upwards as Snowy falls down are shown below. We leave the
rest of the secondary motion as an exercise for you.
Make the animation and play it. The code can be founder under Example9-5,
in files Example9-5.cpp, Snowman.cpp and Sn0wman.h.
Watch the animation and take special note of the tricks you have used in this
section to make the animation more fun and realistic. Particularly notice the
change in camera viewpoint, the roll of Snowy's head, his eyes widening, and his
hand movements, too. Interested readers should also try changing the
interpolation technique for Snowy to be non-linear. What do you see? Is the
motion better or worse than its linear counterpart? Add shading and lighting to
your animation as well for a cool animation.
There are several other animation principles that can be applied to achieve
realistic motion. Exaggeration or deliberate amplification of an idealaction is
often employed to focus the viewer's attention. Another common technique used
for achieving a subtle touch of timing and movement is called slow in and slow
out. In this the object slows down before reaching a key frame. We saw how this
makes for a natural look of the bouncing ball when it reaches its highest point.
The use of all these animation principles, along with good presentation of a
theme, can help produce eye-catching and entertaining animation.
9.4 Advanced Animation Techniques
Interpolation is just one of the techniques used to generate realistic animations.
Depending on the effects desired, different kinds of animations can be combined
to achieve desired effects. We discuss some of the more advanced animation
techniques briefly in this section.
Dynamics
Dynamics is the branch of physics that describes how an object's physical properties
(such as its weight, size, etc) are affected by forces in the world that it lives in (like
gravity, air resistance, etc). To calculate the animated path, an object traversing the
virtual 3D world is imbued with forces that model after the physics of the real
world. These forces act on the models causing some action to occur. For example,
blades of grass sway in the wind depending on the speed of the wind, and to a lesser
extent, depending on the size of the blade.
In the 3D world, a wind force can be defined which when applied to models of
grass, affects their transformation matrix in a manner defined by the laws of
physics.. This kind of simulation was used in A Bug's Life, where an infinite number
of grass blades were made to constantly sway in the background by defining wind
forces on them. The foreground animation of Flick and his troop was still defined
using Key Frame animation.
Procedural motion
In this technique, a set of rules is defined to govern the animation of the scene.
Particle systems are a very good example of such a technique. Fuzzy objects like
fire and dust or even water can be represented by a set of particles. These particles
are randomly generated at a defined location in space, follows a certain motion
trajectory, and then finally die at different times. The rules define the position,
motion, color and size of the individual particle. A collection of hundreds of
Motion Capture
Real-time motion capture is an advanced technique that allows animators to
capture real-live motion with the aid of a machine. An actor is strapped with
electronic nodes that transmit signals to the computer when the actor moves. The
computer records this motion. The captured motion data can then be applied to
the transformation matrix of the computer-generated characters to achieve a real
-live effect.
Kinematics
Kinematics is the study of motion independent of the underlying forces that
produce the motion. It is an important technique used to define articulated fig.
animations.
Usually, a defined model is applied that has a skeleton structure consisting of
a hierarchy of rigid links (or bones) connected at joints. Each link corresponds
to certain patches of the model surface, also called the model skin.A joint can be
rotated, causing the corresponding surface and the attached children bones to
rotate about it. There are usually some constraints on how much each joint can
be rotated.
This motion corresponds closely with the human anatomy, and hence is used
very often for human like animations.
Consider the model of a leg, with a defined skeleton consting of three links
L1, L2, and L3 at joints J1, J2 and J3 as shown in Fig.9.13. J1 is the head of the
hierarchy. A rotation about J1 causes Ll-J2-L2-J3-L3 (and the associated skin)
to rotate as shown in Fig.9.14. You can also rotate about joints 52 or 53 to achieve
a desired position.
Can you see the relation between this organization of joints and links and the
human (or any other animals) leg? Can you visualize how this technique could
LS
Summary
In this chapter, we have looked at a few of the common animation techniques
used to bring 3D models to life. We have seen the classic example of a bouncing
ball and how different interpolation techniques yield different animations. We
looked at some advanced animation techniques that can make the life of an
animator simpler.
3D animation can be very drab if we do not apply the principles of traditional
animation. We have studied these principles and used them to animate Snowy,
our friendly snowman. In the next chapter, we will see how to animate the
camera to produce a stunning game. In Chapter 11, we will create a small movie
using Maya.
Chapter 10
Viewpoint Animation
In the last chapter, we learned some tricks to make our animations look more
realistic and appealing. In all the previous examples, the position of the camera
or our viewpoint was fixed. We only did a one-time change to the camera
position: a cut in the animation sequence. Camera movement is not limited to
changing across shots only. A technique often employed in animation is to move
or transform the camera itself through a 3D world. This technique is called
viewpoint or camera animation.
Why would one want to animate the camera? Well, camera movement adds
valuable depth to the scene. Moving the camera through space causes objects to
move in different ways depending on their distance from the camera, cluing us
in to how they are spatially arranged in our 3D world space.
In games, the camera represents the player's eye: he can change his or her
viewpoint by moving the joystick or the keyboard. A lot of planning is required
to move the camera in a game. The speed, field of vision, and orientation of the
camera are critical because what the camera sees is what an observer would
perceive. The faster the camera moves the faster the observer feels he or she is
moving. The sense of speed due to change in position and orientation obtained
by camera motion can produce eye-catching and exciting effects. These
techniques are commonly used in games and motion ride simulators, where you
feel like you are moving through a real-life scene. Other spectacular examples of
viewpoint animation can be found in animations of the solar system, the motion
of atomic particles in a virtual model, etc.
What you will learn in this chapter:
How to move the camera through space
How to capture keyboard input to change camera positions
w How to implement your own 3D game complete with camera motion
and fog
244 10 1 ANIMATING
THE CAMERA
IN THE SNOWYANIMATION
Let us begin this chapter by seeing how to move the camera in our snowy
animation.
For the next 20 frames, the camera moves smoothly to its new position:
To prevent any drift between frames 50 and 70, we define an extra key frame
at frame 70:
Finally, in the Display code, we interpolate the camera position and set the value
accordingly. Note that we only interpolate the position of the camera.
Voila! Watch your snowman animation, with a smooth camera pan just before
Snowy falls from the cliff. Does it help the animation in any way? Already you
get a sense of perspective between Snowy and the plateau on which he is
jumping. If there were more objects in the scene, the sense of perspective
between the various objects would be even keener. The entire code can be found
under Examplel 0-I/ExamplelO-1 .cpp.
In ExamplelO-2, we use the glTransform routines to simulate camera
motion. We define the key frames as before, but we actually will be using the
rotation to simulate camera rotation as well. From frames 0 to 30, the camera is
at the same position as in Examplel 0-1.
246 10 2 BUILDING
UP A HEAL rlME 3D GAME
From frames 30 to 50, we move the camera and rotate it slightly along the x- and
y-axis to stage Snowy in the shot:
Watch the camera zoom in on Snowy. In general, this technique is easier for
animating the camera, and we shall use it more in the next few sections.
Camera Definition
First, let us see how to define our camera. In the last example we used a nine-
element array to hold the camera position. In this example, it will be easier to
think of the camera in terms of its position and orientation exclusively: it is rare
for someone to scale a camera in the middle of a game. We define a structure for
the camera as follows:
CAMERAINFO camera;
void InitCameraPosition(CAMERAlNF0*camera){
--
camera-> posiiion[O] 0;
camera->position[ll 0.2;
-
camera->position[2] 0 ;
that is, slightly above the ground and looking down the z-axis.
To apply motion to the camera, we simply apply the gl Transforms we saw
earlier, using the current camera position:
void MoveCameraPosition(CAMERAlNF0camera){
glRotateflcamera.orientation[21,0.,0.,1 .I;
glRotatef(camera.orientation[ll, 0.,1.,0.1;
glRotatef(camera.orientation[0], 1.,0.,0.);
glTranslatef(-camera.position[O],-camera.poiion[l], -camera.position[211;
1
The main question is this: How do we update our camera position based on user
input in real time?
248 10 2 BUILDING
UP A REAL TIME 3D GAME
Camera Update
To update the camera position, we make use of the popular physics concept:
SHORT GetAsyncKeyStatelintvKeyl
This function checks the state of the key defined by the parameter vKey-is
it being pressed or not. Unix platforms provide a similar library call.
For simplicity, we define our own Boolean macro:
This function tests whether the key identified the specified vkcode is currently
being pressed down and returns 1 if it is. For the up, down, left and right arrow
keys, the vkcode ID is defined as: VK-UP and VK-DOWN, VK-LEFT and
VK-RIGHT. Using this function, we can set up a test to determine key state and
identify the velocity appropriately. Note that only one key is assumed to being
pressed at a given tick.
if lKEYDOWN(VK-UP))
-
velocity 0.4f;
if (KEYDOWN(VK-DOWN))
-
velocity -0.4f;
if (KEYDOWN(VK-LEFT))
-
avelocii -7;
if IKEYDOWNIVK-RIGHT11
-
avelocity 7;
In this example we are driving the viewer along a terrain. The viewer cannot lift
himself off the ground (motion along the y-axis). He also can only rotate his
viewpoint about the y-axis (along the x-z plane, called a roll), but not along the
x-axis (called a pitch) or z-axis (called a yaw). If you want to simulate a flying
motion, you will have to enable this motion as well.
The distance traveled along the x- and or z-axis as a hnction of the total
distance traveled depends on the orientation of the camera position as shown in
the Fig. 10.1.
void UpdateCameraPosiiion(CAMERAlNF0"camera1 {
LARGE-INTEGER current'lime;
GLfloat elapsedTime;
GLfloat velocity- 0;
GLfloat avelocity- 0;
-
Glfloat d 0;
--
camera->orientation[Ol 0.;
camera->orientation[ll + avelocii*elapsedTime;
-
d elapsedTime * velocii;
-
camera->orientation[21 0.0;
--
camera->position[O] + d * ((GLfloat1sin(TORADIANS(camera->orientation[1]111;
camera->position[ll 0.2;
camera->position[21-- d * ((GLfloat)(coslTORADIANS(camera->orientation[ll111;
1
Note that the math functions sin and cos require the angles to bc in radians,
hence the conversion. A timer thread is used to call the Display function at every
tick. The function first updates the camera position, and then draws the world
from this new vantage point.
void Display(void1
The Ground
The extents of the ground plane extend from -100 to 100, and it is defined as
follows:
void draw-GroundO
{
Some Clutter
We randomly throw in a few cylinders to clutter up the scene. A cylindrical
model can be created by using the glu library command
glucylinder 1 GLUquadric* quad, GLdouble base, GLdouble top ,GLdouble height, GLint slices, GLint
stacks I;
The quadric object need only be defined once using the function
In our game, the cylinders are drawn with a texture map wrapped around them,
as shown in Fig.lO.3. This map causes the cylinders to look like buildings with
lit-up windows
You can make the game a lot more interesting by defining more interesting
and complicated models in the 3D world.
Wefine TANOY 1
Wefine TCYLINDER 2
The models are stored in an STL vector map of thc MODELINFO struct.
vectorcMODELINF0 *> Models;
void InitWorldO{
int i;
SYSTEMTIME systime;
GetSystemTmeN?tsystime);
11 Seed random number generator
srandIsystime.~Minute*60+ systime.wSecond);
noofshapes - ReadVRML("..\\ModeIs\\robot.wrl", &coords[Ol[Ol,
&normals[01[0],&indices[0][0],&nindices[0][0], &(noofpoly[Ol), MAXSHAPES, MAXCOORDS);
- -
Andy->position[Zl 0;
Andy->type TANDY;
Models.push backIAndy1;
-
cylinder g l ~ ~ e w ~ u a d 1;r i c l I* Allocate quadric object *I
gluQuadricDrawStyle( cylinder, GLU-FILL 1; I* Render it as solid *I
for (i-O;i<25;i+ +I{
-
Cylindeail IMODELINFO *)malloc (sizeofiMODELINF0));
-
Cylindeail-> position[Ol RandomPosO;
--
Cylindeail->position[ll 0;
Cylinder[#> position[ZI RandomPosO;
-
Cylinder[il->type TCYLINDER;
Models.push-back(Cylinder[il);
1
The code for the drawworld fbnction is shown below. It simply draws the
ground, and then loops through all thc models dcfined in the Models vector,
calling the appropriate drawing routines.
void drawWorldO{
vector<MODELINFO *>::iterator it;
gITexEnvfIGL-TEXTURE-ENV, GL-TEXTURE-ENV-MODE, GL-DECAL);
- ---
for (it Models.begin0; it! Models.end0; + +it){
if ((*it)->type TANDY)
draw-Andyll*it)->position);
else if (I*itl->type --
TCYLlNDERl
draw-Cylinder(l*it)->position);
1
Object Culling
Remember that our camera is defined as a frustum with a clipping range of about
50 units:
The easiest way to check whether (entire) objects are within the camera view is
to approximate the viewing range with a sphere. We can define the sphere that
encompasses the viewing frustum as a sphere located at the approximate center
of this frustum
with a radius of 50 units. Any object that is not within the boundaries of this
sphere will be eliminated. Recall from math that the distance of point (x,z) from
a center (cx,cy) can be computed as
If this distance is less than the radius of our sphere, then we are within the
viewing distance (or close to it); otherwise we are outside. We can use this test
to determine whether the object is positioned within this sphere or not. The code
return FALSE;
1
returns TRUE if an object located at a position defined by the arraypos is inside
the viewing sphere.
The drawworld code is now modified to test if objects are within the viewing
sphere. If not, we do not make the openGL call to draw them, effectively culling
them from the scene.
NodeA
Boundary: (-5. 5) in X and Z
I Spherel
center:(-0 5,O 5 ) radius = I
II Sphere2
center:(-4,-5)radius = I
1
Fig.10.5: Scene Tree
The function
glFogilGL-FOG-MODE, fogfilter);
glFogfv(GL-FOG-COLOR, fogcolor);
will establish how close to the screen the fog should start. You can change the
number to whatever you want depending on where you want the fog to start. The
command
This tells the OpenGL program how far into the screen the fog should go.
Objects farther than this distance from the camera cannot be seen and hence can
be culled out. The code below defines fog for our game using OpenGL.
Run your program with fog enabled and watch what it does to enhance the
mood of your game. An image from our game is shown in Color Plate 6.
There are many more bells and whistles you can add. You can have rooms
and dungeons set up for the viewer to explore. Andy himself may not be
stationary-he may be animating about the scene, trying to avoid you. Or, he
may be firing bullets at you! You have to hide behind an object, locate him and
fire back. If you hit him first, you win! Otherwise ...
Summary
In the last few chapters, we looked into the principles of animation and how to
bring our models to life. In this chapter, we shall put together all that we have
learned to develop our own animated movie using Maya PLE.
We shall go through the development of the movie, just as the pros do-from pre-
production, where the story is developed; to production, where the movie is
developed; to post-production, where the frames are polished up and finally put
onto tape..
By the end of this chapter, you will know all the industry buzz words and secrets!
In this chapter, you will learn
Steps taken to develop a movie-from pre-production to post-production
Animation using Maya
Create a fully rendered movie sequence using Maya
For this book, let us design a movie called A Fish Story. The audience is you,
our readers. We do love the story, and hope you will love it as well.
The story line is as follows:
A fish is swimming in a fish bowl. Adjacent to the fish bowl is a martini in a
glass. The fish decides it's thirsty, and it wants martini, not water! It jumps over
to the martini glass: only to find the glass is filled with water too!
Storyboards
We saw storyboards in Chapter 4. Let us explore them in more detail in this
chapter. The concept of storyboarding is rather simple. Storyboarding is a way
for you to organize and preplan your project before attempting to actually
develop it. The storyboarding phase helps identify the shots of the story in
greater detail. It identifies the key frames of the shot, the layout of the characters,
and the general ambient setting and mood of the scene. It also identifies the
rough timing of the shots.
The storyboard is divided into shots. Each shot has key frames that are drawn
out to represent the essence of that shot. The task is to move your story, from
frame to frame, filling in the details.
You don't have to be an artist to storyboard. If you have ever looked at
storyboards by Hitchcock or Spielberg, you have to admit that they can't draw.
Storyboarding is especially usefbl for complex visual sequences e.g. elaborate
shots or special effects sequences.A detailed timing chart is also made at this point.
For our fish story, we identify five shots as described below. The story boards for
the shots are also shown. The timing is calculated based on the way the story
boards play out. It takes a lot of experience to predict good timing. Trial and error
is part of this process.
w Shotl: Opening shot: the camera pans in to show the fish swimming in
a bowl on a table. A martini glass sits next to the bowl. Shotl ends with a close-
up of the fish wiggling his eyes. He has an idea! We define Shotl to be 70 frames
long -just a little more than 2 seconds.
wiggle
Shot4: Camera pans back. The shot shows the fish leaping and reaching
the martini glass. Shot4 is 60 frames long.
Into glass
h!? - - - _ _ - --
Oh no ! Only water
Remember, the point is to get an idea of how things will look on screen.
2) Copy a set of storyboard sheets so you don't have to spend all night
drawing screen boxes.
3) Sketch in pencil so you can make changes easily. Then ink in for
photocopying. Feel free to use any medium you are happy with
professional storyboard artists use everything from magic markers to
charcoal.
4. Scribble down short notes about what's happening in the shot (e.g.,
"Bob enters") what the characters are saying ("Is this it? Is this how...")
or what kind of sound effects you want ("Roll of thunder").
5. Number your shots so that they can be quickly referenced to on the
shot list and during editing.
11.2 Production
Production is the phase of the project where the actual movie content is
developed. In the CG world, production means: the modeling of the characters
and sets, laying out the shots (composing the world), animating the models
within the shots, and finally lighting the scene to achieve the desired ambience.
In the end, you render the shots in order to see your animation in it's full glory.
Start up Maya. Let's get ready to rumble.
Modeling
Based on the storyboards and character design, the characters and sets of the shot
are modeled. The models can be input into the computer in different ways. Some
models are first sculpted in clay and then input into the computer using 3D
scanners. Some models are directly modeled in the computer using software like
Maya.
Materials can also be assigned to the models at this point.
We already have most of the required models (sized appropriately) from
Chapter 8.
We have created the fish and fish bowl model which you can import in:
Models/bowl.mb and Models/Jish.rnb. Interested readers should try and model
their own versions of the fish character.
Layout
The layout phase involves putting together the models to compose the 3D world.
We saw how to lay out some of the world in Chapter 8. Let us put the fish and
fishbowl into the scene as well.
1) Start up Maya.
2) Load in the world you created in Chapter 8. If you don't have one, load
in our model from Models/world.mb.
3) Remove the second martini glass that we had created-we need to make
room for the bowl!
4) Import bowl.mb.
5) Set selection mode to be hierarchy and select the bowl group.
6) Translate the bowl to (-3.7 ,3.1, -0.46) and set its scale to be ( 0.7,
0.7,0.7).
7) Set the selection mode to be Object.
8) If not already assigned, select only the outside bowl and assign it the
glass shader that already exists, using the marking menu.
9) Assign the water inside the bowl to the water shader.
10) 1mportfish.mb. We have already assigned materials to the fish.
11) Select the entire fish group and translate it to (-2, 2.75,O).
12) Save your scene in a file called worldwithfish.
Animation
Now comes the h n part! Now we get to actually make each shot of our movie
come alive.
Most animation systems (Maya included) use the frame as the basic unit of
measurement for animation sequences. Each frame is played back in rapid
succession to provide the illusion of motion. The frame rate (frames per second
or Qs) that is used to play back an animation is based on the medium in which
the animation will be played back (for example, film, TV, video game, etc.) We saw
that film requires 24 Qs to depict smooth motion, whereas a TV requires 30 fps.
When you set a key frame (referred to as just key in Maya), you assign a
value to an object's attribute: for example, it's translation, rotation, scale, color,
etc., at the specified frame. When you set several keys for an object with
different values, Maya will interpolate the in-between frames as it plays back the
scene. The result is the movement or change over time of those objects and
attributes-animation!
Let's setup Maya for animation.
1) Select the Animation menu set so that the animation related menu
appears.
2) Select Window > Settings/Preferences > Preferences from the menu bar
In the Preferences window that pops up, click the Settings category and
set the Time option to NTSC(30fp) so your animation will play at the
rate of 30 frames per second. Click the Save button.
At the bottom of the Maya UI is the animation timeline, called the Time Slider
in Maya lingo. Look over the playback controls, as shown in the figure below:
Time Slier Playback Conuds
h
\
PlaybackStan Time Playback End n m e
/
Animation Preferences
Range s l i e r
The Time Slider displays the frames of the animation along a time line. The
key frames that you set for the selected object are displayed as red lines along
the time line.
The Range Slider controls the range of frames that play when you click the
play button. It displays the start time and end time of the entire animation, as well
as Playback Start Time and Playback End Time in text boxes. The playback
times may vary depending on which shot you want to view.
The box at the top right of the Time Slider lets you set the current frame
(time) of the animation.
The Playback Controls control animation playback. You may recognize the
conventional buttons for play and rewind (return to the start time). The stop
button appcars only when the animation is playing. To find out which operation
a buttonttext-box represents, hold the mouse pointer over it.
The Animation Preferences button displays a window for setting animation
preference settings such as the playback speed.
The above items will have more relevance as you work through this lesson.
In the End Time box, set the End Time to be 250. This sets the length of the
entire animation to bc 250 frames long.
Shot1
First, we will set up shotl, which is 70 frames long. In this shot, we will animate
the fish. For 60 framcs, Mr. Fish will swim till the end of the bowl, turn around
and swim back up again. In the next 10 frames, Mr. Fish raises his eyebrows to
indicate he is eyeing somcthing interesting.
Click the rewind button to go to the start of the playback range. This
changes the current frame to 0 (some systems use 1 as the start frame).
Make sure the selection mode is set to hierarchy.
Select the Fish.
Choose Animate > Set Key from the menu bar. This sets up the first key
frame for the fish at the position defined. Notice the red marker that
appears in the Time Slider at frame 0, known as a tick. Ticks in the Time
Slider indicate the key frames for the currently selected object.
At frame 25, we want Mr. Fish to reach the end of the bowl. Go to frame
A convenient way to do this is to click at the desired frame in the Timc
Slider, or to enter this value in the Current Time box.
With the fish selected, go to the Channels Editor and set it's
Translatex=-5.
Select Animate>Set Key to define this kcyframe. Make sure you see the
tick at frame 25.
For the next 10 frames, we want the fish to turn around. Go to frame 35.
Set the fish's RotateY = 180. Set the key frame by selecting
Animate>Set Key. (There is a shortcut to setting the key frame-just
hitting the "s" button. However we find that this doesn't always work
very well.)
At frame 60, the fish reaches the other end of the bowl. Go to frame 60.
Set the fish's Translatex = -2 and set the key frame.
10. Play your animation by clicking the play button from the
Playback Controls.
From the three keys you've set, Maya creates motion between the positions.
By default, the animation plays in a loop from frame 0 to 60. The animation
occurs in the active window. If you have a fast computer, you might notice that
the animation plays too fast. By default, Maya plays the animation as fast as it
can be processed. Because this scene is simple, the animation might play faster
than the default rate (30 frames per second). When you render all the frames of
your animation for final production, the animation will be smooth. You can also
drag the mouse back and forth (scrub) in the Time Slider to see the in between
frames as you drag the mouse. This is the most basic aspect of our motion.
Following the principles of animation, we can now enhance the animation. First,
let's put in some secondary motion to make the movement look more believable.
Secondary Motion
The tail of the fish should swish as it swims around.
1. Rewind the animation.
2. Set the selection mode to Object.
3. Pick only the tail of the fish (you can pick it from the
Windows>Outliner window as well, the object is called Tail and is
under the Fish parent object.)
4. At frame 0, set the Tail to have a RotateY=20. This will swish the tail
to one side. Set the key frame.
5. At frame 10, set the RotateY to -20. This will swish the tail in the other
direction. Set the key frame.
6. Repeat steps 4-5 for frames 20,30,40,50 and 60. This will cause the tail
to swish from side to side when you playback the animation.
Our shot now calls for a camera pan to zoom into Fishes expression - what
we call staging. To do this, we actually need to create a new camera that can be
animated. (Maya does not allow the default cameras to be animated.)
Staging
1. Choose Create>Cameras>Camera from the main menu.
2. A new camera will appear.
3. Open the Attributes Editor. In the Attributes Editor, you will see the
transformation attributes as well as a tab section to define the camera
settings of this camera.
4. Rename the camera as Animationcamera by clicking inside the box
with the camera name.
5 . Set the transforms of this camera to be Translate =( 6., 8, 14),
Rotation=( -10, 30, 0).
6. Choose the four panel layout (if it is not already chosen).
7. We don't need the side view window, so we will replace it with the view
seen by our Animation camera.
8. In the menu bar of the side-view window, choose Panels>Look through
Selected. Since the AnimationCamera was selected, this window will
now show the world from the viewpoint of the animation camera.
9. Click into this window and hit 5 to view the objects in a shaded mode.
10. Play the animation with the new camera view selected.
This will be the camera we use for our movie. The animation of a camera is
similar to animating objects. We need to define its key frames, and Maya will
perform the interpolation for us.
We want the camera to be stationary for the first 30 frames. For this, we need
to set a key frame at frame 30. From frames 30 to 60, we will let the camera
zoom into Mr. Fish.
11. Go to frame30.
12. Make sure the transforms of the Animation camera are still defined as
Translate =( 6, 8, 14), Rotate=( -10, 30, 0).
13. Make sure that the animation camera is the selected object, and hit
Animate>Set Key to define this key frame.
14. At frame 60, define the key frame for the animation camera to have
Translate= (-0.883, 3, 2). Keep everything else the same.
Now play your animation (with the animation camera window selected). You
will see the camera zoom on the fish.
6. Pick Eyebrow2 from the Outliner Window. Go to frame 60. Make sure
the transforms are set as Translate=( 0.063 , 0 , -0.109) and Rotate=(O,
16.866,O). Save this default position as a key frame.
7. At frame 70, raise the eyebrow by setting Ty = 0.05. Leave the other
transform as is and set the key frame.
8. Select the AnimationCamera. Set frame 70 as a key frame with its
current transformations. This will ensure that the camera stays still till
frame 70.
Play your animation. You will see the fish looking up in delight. What has he
seen? You can enhance the eye motion even further by making the eyebrow
move up and down a couple of times and by widening the eye. We leave this as
an exercise for the reader.
Shot2
Shot2 is a cut to the martini glass. It extends for 20 frames. Its main purpose in
the film is to make the viewer aware of the fact that it was the glass that the fish
saw.
1. At frame 71, we will cut to the martini glass. To do this, set the
transforms for the camera as Translations=(1.06, 2.436, 5.382) and
Rotations=(O, -30, 0) and save the key frame.
2. Go to frame 90 and save the key frame again to keep the camera
stationary between frames 71 and 90.
Shot3
In shot3, we cut back to the fish. For continuity reasons, the camera position is
the same that we ended at in shotl. Shot3 shows the fish excited and getting
ready to launch out of the fish bowl. It extends from frame 91 to frame 140.
1. At frame 91, we cut back to the fish. Go to frame 91. Set the animation
camera transforms back to Translation = (-0.883, 3,2) and Rotation =( -10,
30,O). Save the key frame.
2. Go to frame 140 and set this as a key frame with the same transforms to
keep the camera stationary during the shot.
The fish saw it! His eyebrows are wiggling.
3. Pick Eyebrow2. At frame 91, lower the eyebrow by setting Translate
Y = 0 and set this key frame.
4. At Frame 94, lift the eyebrow by setting TranslateY = 0.05. Save the
key frame.
5. Lower, lift and lower the eyebrows at frames 97, 100, and 104.
Let's get Mr. Fish to squash and stretch his body in anticipation of a leap.
6. Change the selection mode to hierarchy and pick the fish (make sure the
entire hierarchy is chosen).
7. Go to frame 111 and save the fish's current transformation as a key
frame.
8. Set a key frame at frame 130, by setting its Rotate=(O, 180 50), and
Scale = (0.7, 1.2,l). Do not change the other transforms. This will squash
the fish.
9. At frame 140, set a key frame by changing the fish's Scale=(1.2,0.8,1).
This will stretch the fish in anticipation of the leap.
10. Play back the shot to view your animation. Again, you are encouraged
to use your creativity in enhancing the motion to make it more believable.
Shot4
The fish is ready to fly. He wants some of that martini and he wants it now! In
this shot, we shall pan back the camera so we can see the entire flight of the fish
from bowl to glass. The shot lasts from frame 141 to frame 200.
1. At frame 141, we want to pan back to view the entire scene and see the
fish leaping.
2. Set frame 141 as a key frame for the animation camera, with its
transformations set as Translate= (6,6,14) and Rotation=(- 1O,3O,O).
3. Go to frame 200, and save the current settings of the camera as a key
frame as well.
Let's make the fish fly!
4. Pick the fish. At frame 141, set a key frame with the fish set at Translate
= (-4.5,2.75,0), Rotate = (0,180,70) and Scale = (1.2,0.8,1)
5. At frame 170, set the key frame for the fish as Translate = (0.5,8,0),
Rotate = (0,180,O) and Scale = (1,1,1).
6. At frame 200, set the keyframe for the fish as Translate = (3,5,0), Rotate
= (0,180,-60) and Scale = (1.2,0.8,1). Almost in the martini glass!
Shot5
Shot5 is our finale. It shows the fish landing in the glass. But did he really get
what he wanted?
For continuity reasons, we start the shot with the fish just about to land in the
glass. Shot5 lasts from frame 201 to frame 250.
1. At frame 20 1, select the fish, and set its transforms as Translate = (3.1,
4.45, 0.47), Rotate = (0,180,-50) and Scale = (0.6,0.,6,0.6). The fish is
about to land in the glass. The fish is a bit bigger than the glass, so
we have to cheat on its size a bit! Set the key frame.
2. At frame 210, the fish has landed in the glass. Set frame 210 as a key
frame with the fish transforms as Translate = (3.44,3.466,0.476) and
Rotate = (-60,120,-50).
3. Oh no! It's only water!
4. Till frame 250, you may want to swish the fishs tail slowly, so the scene
doesn't look completely still.
You can add in hrther animation detail to display the sadness that the fish is
probably feeling at the end result of his effort. His eyebrows drooping or his eyes
going half closed are great ways to convey a mood of sadness. What other
motion can you think of!
1 Edit Help
T ~ m eRange r Time Slider 5 StarltEnd
StartTirne
EndT~rne-1 I -
Vtew /;;
Show Ornaments r -
V~ewer(i Movieplayer r fcheck
vv
Dtsplay S~ze From Wtndow T
Lights
Lights are used for much more than just illumination of the scene. The lighting
in a shot can frame the characters, punch up the main idealmodels of the shot,
and define the mood of the scene. Wow!..and you thought lights were an
afterthought.
In Chapter 8, we defined certain mood lighting that matches what we wish to
portray here. We shall keep much of that same lighting. We add in a couple more
lights to punch up the fish, and the martini glass.
To punch up the main characters, we define three spotlights.The first
illuminates the fish bowl and the martini glass from thc front. A top view of the
layout is shown in the Fig.1 1.6
Two spotlights illuminate the models from the back. The back lighting is
defined so that the reflections of the models in the mirror are illuminated as well.
All three lights have a soft white color with an intensity of 0.5.
It's a good idea to render a few frames from every shot and make sure they
are rcndering as desired. Usually, every shot is fine tuned for lighting and
material settings to make sure it looks just right!
We cheated on the light coming from the lampshade, so that our shadows
I
Fig.ll.6: The top view
came out exactly where we wanted. Another point we noted while rendering our
scenes: the fish was casting shadows in the glass bowl as well as in the water.
This effect was not very pleasing, so we turned off the ability for the glass and
water materials to be able to receive shadows. You can do this by selecting the
object, going to the Attributes Menu and turning off the radio
button for Receive Shadows under the Render Stats option. Once you are
satisfied with the test renders, we can set the frames off into batch render mode.
Render
Finally!! It is time to render all those images. Video resolution requires images
be rendered with a resolution of 640 by 480. Film requires 1K resolution or
higher.
We shall render our images to be 640 by 480. Maya has a batch render mode that
allows you to render the entire animation sequence.
To set up the rendering job, proceed as follows
1. From the shelf, select the Render Globals Window icon to display the
Render Global Settings window. In the Render Global Settings window,
select the Common tab, and then open the Image File Output section.
2. In the Image File Output section, set the following options:
a. File Name Prefix: Enter the namefish. This name will be the base
of the filenames created by batch rendering. On top of the Render
Globals window, you will see the Path: variable. This defines the
exact folder in which the rendered files will be placed. Make a note
of it.
b. FrameIAnimation Ext: Select name.#.ext. This specifies that the
filenames will have the format prefix.frarneNumbe~fileFormat.
For example, batch rendering the entire 240-frame animation will
create fish.OOO1.iff, fish.0002.iff7and so on through fish0240.iff.
c. Start Frame: Enter 0, the first frame of the animation sequence to
be batch rendered.
L Ovss
-I
d. End Frame: Enter 250, the last frame to be batch rendered.
e. Frame Padding: Enter 4. This causes the frameNumber part of the
filenames to be four digits prefixed with 0s. For example, the
filenames will fish.OOO1.iff tinstead of fish. 1.iff.
f. Set the renderable Camera to be Animationcamera. This is the
camera which we will be rendering.
3. For the remaining options in the Render Globals, you'll use the
settings you defined from Chapter 8. Maya will render using
resolution ( 6 4 0 ~ 4 8 0and
) ~ anti-aliasing quality (Production
Quality) with ray tracing on.
From the menu bar, choose Render>Batch Render to kick off your render job. It
will take a while-maybe even hours-so you take a tea break before you view the
final rendered animation.
The post production phase is when editorial comes in. Editors pick up the
animated frames and compile them together to make the final movie. The sound
track, complete with narration, music, and sound effects, is added into the clip.
If the animation is to be mixed in with live action footage then the live action
footage is digitized and composited with the animated sequence as well.
The key to all of this is non-linear computer editing.
In traditional video editing folks would copy segments of your tape to a
master tape, laying shots in order linearly onto the tape. If you got to the end of
a sequence and decided that you wanted to change the third shot, you would have
to edit the entire sequence all over again. Grrr!
Non-linear computer editing allows you manipulate movie clips and audio
(animated or otherwise) in the same way that you would add and change words
in a word-processor: simply Cut, Copy and Paste the shots to stitch your movie
together.
If you want to do this at home, you will need to get a video capture card,
which will allow you to sample and store clips of your video to hard disk. You
will need to digitize your clips frame by frame to composite them with an
animated sequence.
Non-linear editing software usually comes bundled with the card. Get a
powerful, flexible package that is easy to work with. Some worthwhile packages
are Adobe Premiere, Ulead Mediastudio, Judgement, EditDV and Avid. .The
software will allow you to edit the audio tracks adding voice overs and
background music. You can then use it to the tape the final clip onto film or video
as desired.
For example, in our fish animation, we could use a live-action footage of
people drinking at a party. The footage would need to be digitized into a
sequence of frames. These frames could then be used as images to be reflected
in our mirror (achieved by compositing the live action image and the animated
image with an appropriate mask). This would give the effect of a room filled with
live people. Care must be taken that the lighting of each of the two scenes blends
with the other to make the composite look believable.
Camera
VCT
+r 7
-;-"L. 1
Firewire
I Analog + /
Fig.ll.7: Non-linear editing
Finally, using the non-linear software, you can add in sounds effects and the final
movie clip can be produced and transferred directly to VHS.
The steps followed by professional editorial departments are analogous to the
ones described above. The exact mechanics of non-linear editing is beyond the
scope of this book-maybe in Part 2!
The final images are stored in the directory specified by the Path variable we
noted earlier. Go to this directory. You will see the individual images of the
11.4 Finally: Our Movie
animation saved as fish.*.iff. Once Maya is done generating all the images of the
animation, double click on the fish.0000 image. This will bring up the Fcheck
program that Maya uses to display images and will display the 0th frame of our
render. Choose File>Open Animation and open fish.000.iff again. This will
display the rendered animation of your film. Enjoy!
ColorPlate shows some sample images from our movie. This animated world
is provided for you as well under Models/worldwithJish.mb.
Summary
In this chapter, we have developed a movie following the same processes that the
professionals use. We went from pre-production, where we defined out fish
story; to production, where we animated the movie; and finally to post-
production. We animated and finally rendered a stunning movie using Maya.
This chapter ends our journey into 3D Graphics. We hope you have enjoyed
your experience and are motivated to create your own 3D graphics, using the
techniques you have learned in this book.
Appendix A
OpenGL and GLUT
What is OpenGL?
OpenGL seems to be as close to an industry standard as there is for creating 2D
and 3D graphics applications. OpenGL provides a library of graphics functions
for you to use within your programming environment. It provides all the
necessary communication between your software and the graphics hardware on
your system. The OpenGL API is a portable API that can be compiled and run
on many platforms.
OpenGL programs are typically written in C and C++.
What is GLUT?
GLUT (pronounced like the glut in gluttony) is the OpenGL Utility Toolkit, a window
system independent toolkit for writing OpenGL programs. It implements a simple
windowing application programming interface (API) for OpenGL. GLUT makes it
considerably easier to learn about and explore OpenGL programming. GLUT is
designed for constructing small to medium sized OpenGL programs. While GLUT is
well-suited to learning OpenGL and developing simple OpenGL applications, GLUT
is not a full-featured toolkit so large applications requiring sophisticated user interfaces
are better off using native window system toolkits. GLUT is simple, easy, and small.
The GLUT source code distribution is portable to nearly all OpenGL implementations
and platforms. GLUT is not open source. Mark Kilgard maintains the the copyright.
To run OpenGL programs that take advantage of OpenGL 1.2 or above: you will
need to download vendor-specific OpenGL Drivers for your particular graphics
card. Refer to:
http://www.opengl.org/documentation/spec.
html/
for more details.
File Location
[compiler]\include\g1
[compiler]\lib
[system]
where [compiler] is your compiler directory (such as c:\Program Files\Microsoft
Visual Studio\VC98) and [system] is your Windows 9x/NT/2000 system
directory (such as c:\winnt\system32 or c:\windows\system).
You'll need to instruct your compiler to link with the OpenGL, GLU, and GLUT
libraries. In Visual C++ 6.0, you can accomplish this with the Project menu's
Settings dialog box. Scroll to the Link tab. In the Objectllibrary modules edit
box, add glut32.lib, glu32.lib, and opengl32.lib to the end of any text that is
present.
For Mac OS X:
Apple provides excellent documentation on how to access header and library
files and code using their OpenGL SDK at:
http://developer,apple.com/opengl/
You should now see the following folders and files installed:
Select File > New from the main menu. Next select the Projects tab and select
the Win32 Console Application project. Don't forget to enter the project name
and the path to the project folder.
Enjoy!
Appendix c
Maya Personal Learning
Edition (PLE)
What is Maya?
Academy Award winning Maya software is the world's most powerfully
integrated 3D modeling, animation, effects, and rendering solution. Maya also
adds to the quality and realism of 2D graphics. That's why film and video artists,
game developers, visualization professionals, Web and print designers turn to
Maya to take their work to the next level.
Follow the instructions on how to install the program onto your desktop.
This page also provides more details on software and hardware requirements for
running the program.
Bibliography
What follows is a bibliography in computer graphics. In addition to being a list
of references from the various chapters, it also contains references to books and
journals where the latest research in the field is being published.
BADL91 Badler, N.I., B.A. Barksy, D. Zeltzer (ed.), Making Them Move:
Mechanics, Control, andAnimation of Articulated Figures, Morgan Kaufman, 1991.
BOVIOO Bovik, A,, Handbook of Image and Video Processing, Academic Press,
2000
DERA03 Derakhshani, D., Introducing Maya 5: 3Dfor Beginners, Sybex Inc, 2003
FOLE95 Foley, J.D., A. van Dam, S.K. Feiner, J.F. Hughes, Computer
Graphics: Principles and Practice in C, Addison-Wesley Professional; 1995
GLAS95 Glass, G , B. L. Schuchert, The STL Primer, Prentice Hall PTR; 1995
HILL00 Hill, F.S., Computer Graphics Using OpenGL, Prentice Hall, 2000
LISC03 Lischner, R., STL Pocket Reference, O'Reilly & Associates, 2003
PORT84 Porter T., and T. Duff, Compositing Digital Images, SIGGRAPH 84,
pp. 253-259.
In betweens 216-7
Tweening 2 16
Animation
Cel animation 2 16
cut 216
Interpolation
Linear interpolation 2 18, 220- 1,
222,225-8
Cardinal cubic spline 224,
Principles of animation
Squash and stretch 23 1-2, 270,
Staging 233,267
Anticipation 234-5, 270,
Timing 235-7,261-2
Secondary action 236
Brainstorming 260
Pitch 247, 260
Storyboards 68,261-2, 264
Object culling 255
Scene graph 256
Color Plate 1:RGB colors and the resultant colors when these colors are mixed
Color Plate 4: Cube mapped objects seem like they are reflecting their environment
Color Plate 5: A Ray traced scene showing reflections and refractions