Você está na página 1de 17

1

Week 6 Animation
This week we move to animations. Hopefully, the objects we have created in our previous sessions can
begin to come to life.
Double Buffering and Frame rates
In animation, the speed of our frames and how we buffer our signals are quite important. Buffering
allows us to refresh with more accuracy and possibly less data loss. With OpenGL, double buffering is
provided with this command:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RBG);
The first attribute (GLUT_DOUBLE) will be used in our code examples to provide the double buffering
and make animation possible.
Frame rates are essentially how many frames per second are available or processed. The higher the
frame rate, the larger the amount of data captured and displayed. In particular, the higher the frame
rate, the better the temporal resolution, or the ability to capture moving objects.
The following videos on youtube shows several different frame rates for an indoor and outside parking
lot surveillance camera. This video demonstrates how the details are impacted based on the frame rate.
Frames per second can make a huge difference in security as well as in games.
http://www.youtube.com/watch?v=XRaDV8YADiQ
http://www.youtube.com/watch?v=e54Q1KXRmX0
Refresh Rates
Your computer monitor probably has a 60Hz refresh rate. Refresh rates indicate how fast your monitor
device redisplays or reconstructs the current screen image. Faster refresh rates should reduce flicker
and make smoother motion and animation. While viewing a 30 frames per second media on a 60 Hz
refresh TV or monitor, each frame is repeated 2 times every 30th of a second.
Example Refresh rate Calculation:
Given a TV with a 120-Hz refresh rate, How many times will a 30 frames per second input signal be
repeated in 1/30 of a second.
N = Refresh rate/Frames per second = 120Hz/30fps = 4
Motion Specifications
To simulate a ball bouncing a damped, rectified sine wave can be used. Rectified just means we are
using all positive values since the ball will be hitting a surface and bouncing back. It is damped since each
time the ball hits the surface it loses some of its momentum.

2
The equation to model this action when assuming a phase angle of radians is shown below:
Y(t) = abs(A sin(t + ) e-kt);
A is the starting amplitude, k is the damping factor (a value between 0 and 1, where large values more
quickly decrease the amplitude) and is the angular frequency (2f0). fo is the Sine wave frequency The
angular frequency is determined the sampling frequency and the sine wave frequency. So if we have a
sampling frequency (fs) of 32 Hertz we are taking snap shots once every 0.03125 (1/32 Hz) seconds. If
our sine wave is has a period of 1 Hz then becomes 2* * 0.03125* 1Hz = 0.196349.
Now the resulting amplitude after each time step (t) can be plotted for varying values of k. In the
following plot, the time step is 1/32 Hz = 0.03125 seconds and the fo = 1 Hz. Values for no damping (k=0)
and k=0.4 and 0.8 are displayed for a signal with initial amplitude of 1. Notice you increment the time
step setting t = timestep * .03125 for each iteration. As a sense check, you can create the data
calculations in excel to confirm your math is correct before you move into your OpenGL programming
environment to code.
1.2
1
0.8
abs(sin(x))
0.6

damped k=0.4
damped k=0.8

0.4
0.2
0

As k increases, the damping is stronger resulting in less amplitude for the original signal. If we increase
the Sine save frequency (fo) to 2 Hz, we double the number of oscillations in the same period as shown
below.

1.2
1
0.8
abs(sin(x))
0.6

damped k=0.4
damped k=0.8

0.4
0.2
0

OpenGL Raster Operation Bouncing Object Code


We can use the OpenGL Raster operations to display the x, y positions of a bouncing object that follows
a damped sine wave. In the following displayFcn() code, notice the use of the glRecti() to create 2
rectangles. The initial positions xpos, ypos, xpos2 and ypos2 define the locations on the screen. The
glutSwapBuffers() call is needed to provide buffering for a smooth display.
// Generate the Graphics
void displayFcn(void)
{
// Clear display window.
glClear(GL_COLOR_BUFFER_BIT);
// Set graphic objects color to Red or change for your choice
glColor3f(0.0, 1.0, 0.0);
// Your graphics code here
// Create a rectangle to move about
glRecti(xpos, ypos, xpos+10, ypos+10);
// Set color to Blue
glColor3f(0.0, 0.0, 1.0);
// Create a rectangle to move about
glRecti(xpos2, ypos2, xpos2 + 10, ypos2 + 10);
// Call the SwapBuffers
glutSwapBuffers();
// Execute OpenGL functions
glFlush();
}

Additionally, mouse and Keyboard functions can be used to start and stop the animation. You should
experiment with this code by changing the sin wave frequency and damping constants as well as other
shapes and the keyboard and mouse functions. The animation function is called within the glutIdleFunc()

4
call. In this case, the animation is controlled using the rotateHex function. When designing your own
animation, be sure to call the glutIdleFunc() when starting your animation. Setting the parameter to null
will stop the animation.
// Left mouse starts the animation
// Right mouse stops the animation
void mouseFcn(GLint button, GLint action, GLint x, GLint y)
{
switch (button) {
case GLUT_LEFT_BUTTON:
// Start the animation.
if (action == GLUT_DOWN)
glutIdleFunc(rotateHex);
break;
case GLUT_RIGHT_BUTTON:
// Stop the animation.
if (action == GLUT_DOWN)
glutIdleFunc(NULL);
break;
default:
break;
}
}
// Clicking 1 starts the animation
// Clicking 2 stops the animation
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case '1':
/* 1 key starts the animation */
glutIdleFunc(rotateHex);
break;
case '2': /* 2 key stops the animation */
glutIdleFunc(NULL);
break;
default:
break;
}
}

The rotateHex() function controls the animation results by updating the x and y positions after a short
sleep cycle. Since we have 2 rectangles, we are calculating 2 different sets of coordinates.
// Controls the x and y positions using damped sin wave
void rotateHex(void)
{
Sleep(SLEEPMS);
// xpos
xpos += 1;
ypos = fabs(AMP*sin(2*M_PI*F0*TS*xpos)*exp(-1.0*TS*xpos));
// Resent if get out of window position
if (xpos > WIDTH)
{
xpos = 1;
}

5
// xpos2
xpos2 += 1;
ypos2 = fabs(AMP*sin(2*M_PI*F0*TS*xpos2)*exp(-1.0*TS*xpos2));
// Reset if get out of window position
if (xpos2 > WIDTH)
{
xpos2 = 1;
}
glutPostRedisplay();
}

The final code segment needed to successfully run an animation in OpenGL is the main() function.
Although similar to our previous main functions, notice the glutInitDisplayMode() is set to
GLUT_DOUBLE | GLUT_RGB. Without the GLUT_DOUBLE parameter double buffering and hence
animation would not occur.
void main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
// Set initial Window position
glutInitWindowPosition(100, 100);
// Set Window Size
glutInitWindowSize(WIDTH, HEIGHT);
// Set Window Title
glutCreateWindow("CMSC405 Week 6_1");
// Initialize
init();
// Display function call
glutDisplayFunc(displayFcn);
// Window reshape call
glutReshapeFunc(winReshapeFcn);
// Mouse and Keboard functions calls
glutMouseFunc(mouseFcn);
glutKeyboardFunc(keyboard);
glutMainLoop();
}

The complete source code listing is found in CMSC405Week6_1.cpp. A snap shot of running this
animation is shown below. You can start and stop the animation by using the left and right mouse
buttons or by clicking the 1 or 2 keys.

We can begin to bundle additional OpenGL functions into our code to increase functionality and reduce
the amount of code that needs to be written from scratch. For example, we can use glScalef(), and
glRotatef() functions to scale and rotate the objects we previously created.
In the following code, additional rectangles have been created and OpenGL functionality has been
added to make a different animation. First, a white rectangle is created and positioned at the initial x
and y positions. If this was the only object, the animation would look similar to the previous one as the
object positions are changed based on the damped sin wave.
A yellow square is added next but within its own push and pop sequence where the square is rotated by
an specified angle about the z-axis. The object is then scaled and finally positions are determined by the
damped Sin wave equation. The red square rotates about the z-axis in a clockwise direction. The green
square rotates about the z-axis in a counter clockwise rotation. Finally, a smaller blue square pulsates in
the center having as the rotation for all axis is set to 0 for the glRotatef() call. Setting these values to 0
has the net results of reducing the object in size until it disappears and then reappears.
// Generate the Graphics
void displayFcn(void)
{
// Clear display window.
glClear(GL_COLOR_BUFFER_BIT);
// Set the graphic objects color
glColor3f(1.0, 1.0, 1.0);
// Your graphics code here
// Create one rectangle only impacted by xpos/ypos
glRectf(xpos, ypos, xpos+20.0, ypos+20.0);
// Yellow square
glColor3f(1.0, 1.0, 0.0);
glPushMatrix();

7
glRotatef(angle, 0.0, 0.0, 1.0);
glScalef(xscale, yscale, 1.0f);
// Adding the Damped Sin wave in addition to scale and rotate
glRectf(xpos, ypos, xpos+100, ypos+100);
glPopMatrix();
// Red Square rotating clockwise about z-axis
glColor3f(1.0, 0.0, 0.0);
glPushMatrix();
glRotatef(angle, 0.0, 0.0, -1.0);
glRectf(-20.0, -20.0, 20.0, 20.0);
glPopMatrix();
// Green square rotating counter-clockwise about z-axis
glColor3f(0.0, 1.0, 0.0);
glPushMatrix();
glRotatef(angle, 0.0, 0.0, 1.0);
glRectf(-15.0, -15.0, 15.0, 15.0);
glPopMatrix();
// Blue square rotating
glPushMatrix();
// Setting all components to 0 simulates reduction in object size
glRotatef(angle, 0.0, 0.0, 0.0);
glColor3f(0.0, 0.0, 1.0);
glRectf(-10.0, -10.0, 10.0, 10.0);
glPopMatrix();
// Call the SwapBuffers
glutSwapBuffers();
// Execute OpenGL functions
glFlush();
}
The angle, scale and positions are controlled in the rotateHex() method. Note in the code
below the values to increment as well as method to reset values are provided for the
angle, scale and positions. The resets are needed to keep the display within the window
position constraints and within reasonable angle and scale constraints.
// Controls the scale, angel and positions
void rotateHex(void)
{
Sleep(SLEEPMS);
// Set the scale increments and resets
xscale -= 0.01;
yscale -= 0.01;
if (xscale <=0)
{
xscale = 1.0;
}
if (yscale <= 0)
{
yscale = 1.0;
}
// Set the angle increments and resets

8
angle += 10;
if (angle >= 360)
{
angle = 0;
}
// Set the position and resets
xpos += 1;
ypos = fabs(AMP*sin(2 * M_PI*F0*TS*xpos)*exp(-1.0*TS*xpos));
if (xpos > WIDTH)
{
xpos = 1;
}

glutPostRedisplay();
}

The code found in CMSC405Week6_2.cpp when run will display the following graphic. Animation is
controlled using the left mouse button, right mouse button and the 1 and 2 keyboard keys. Once
again, you should analyze the code, modify it and experiment with it to understand the possibilities
available within this small amount of code.

3D animation
Using the same process as shown in the 2D animations, 3D shapes can come to life. In the following
displayFnc() call a wireframe Sphere is initially placed in the center of the screen. When the animation
starts, parameters to control the x and y positions, scale, rotation and RGB colors are changed. After
each iteration the parameters are slightly different causing the sphere to move around the screen,
resize, rotate and change colors. Notice the glRotatef() call specifies rotation about both the x and z axis.
Also, in the example, the scale is uniform for x, y and z coordinates.

9
// Display Function
void displayfcn() {
// Clear color and depth buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Model view matrix mode
glMatrixMode(GL_MODELVIEW);
// Reset the model-view matrix
// This is done each time you need to clear the matrix operations
// This is similar to glPopMatrix()
glLoadIdentity();
// Colors controlled by tranform function
glColor3f(R, G, B);
// Since we are in ModelView we need to move to World coordinates
// xpos, ypos controlled by tranform function
glTranslatef(xpos, ypos, -7.0f);
// Scale controlled by tranform function
glScalef(scale, scale, scale);
// Rotate as dictated by angle parameter
glRotatef(angle, 1.0f, 0.0f, 1.0f);
// Add a wireSpere mesh with 1.5 radius and 15x15 gridline
glutWireSphere(1.5, 15, 15);
// Double buffering
glutSwapBuffers();
}

The transform() function is used to set and reset as needed each of the parameters used to control the
animation. Notice the rotation angle adjusts by 5 degrees and resets once it is greater than or equal to
360 degrees. The scale adjusts by 0.01 and resets once the value approaches 0. The x and y positions
change by 0.1 each iteration and reset once the values get close to absolute of 3. This value was selected
for this perspective as the sphere begins to move off of the projection screen near this value. Depending
upon your z position and your gluPerspective() parameters, this value may be different for your
applications. The RGB components are adjusted by 0.01, 0.02 and 0.03, respectively. Each is reset when
the approaches 0.
void transform(void)
{
Sleep(100);
angle += 5.0f;
// Resent if get out of window position
if (angle >=360.0)
{
angle = 0.0f;
}
// scale factor
scale -= 0.01f;
if (scale <= 0.1f)
scale = 1.0f;
// xpos
xpos += 0.1f;

10
if (xpos >= 3.0f)
xpos = -3.0f;
// ypos
ypos -= 0.1f;
if (ypos <= -3.0f)
ypos = 3.0f;
// RGB color control
R -= 0.01;
if (R <= 0.0f)
R = 1.0f;
G -= 0.02;
if (G <= 0.0f)
G = 1.0f;
B -= 0.03;
if (B <= 0.0f)
B = 1.0f;
glutPostRedisplay();
}
As before, the mouseFcn() function sets the glutIdleFunc control. In this case, the
control is found in the transform() function.
void mouseFcn(GLint button, GLint action, GLint x, GLint y)
{
switch (button) {
case GLUT_LEFT_BUTTON:
// Start the animation.
if (action == GLUT_DOWN)
glutIdleFunc(transform);
break;
case GLUT_RIGHT_BUTTON:
// Stop the animation.
if (action == GLUT_DOWN)
glutIdleFunc(NULL);
break;
default:
break;
}
}

For 3D shape redraw and viewing, the gluPerspective should be set in the windowReshapeFnc().
// Windows redraw function
void winReshapeFcn(GLsizei width, GLsizei height) {
// Compute aspect ratio of the new window
if (height == 0)
height = 1;
GLfloat aspect = (GLfloat)width / (GLfloat)height;
// Set the viewport
// Allows for resizing without major display issues
glViewport(0, 0, width, height);
// Set the aspect ratio of the clipping volume to match the viewport
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Enable perspective projection with fovy, aspect, zNear and zFar

11
// This is the camera view and objects align with view frustum
gluPerspective(45.0f, aspect, 0.1f, 100.0f);
}

A snapshot of running the code found in CMSC405Week6_3.cpp is shown below:

Adding an on screen menu text for directions is fairly straight forward. You have to first define the
coordinates and content of the text. The following code will provide directions for running a sphere
animation.
void displayDirections(void){
// Directions
glColor3f(1.0f,1.0f,0.0f);
renderBitmapString(-0.8, 2.7, (void *)font, "Sphere Animation v1.0");
renderBitmapString(-0.8, 2.55, (void *)font, "press r to Rotate");
renderBitmapString(-0.8, 2.4, (void *)font, "press s to Scale");
renderBitmapString(-0.8, 2.25, (void *)font, "press t to Translate");
renderBitmapString(-0.8, 2.1, (void *)font, "press c to Change Colors");
renderBitmapString(-0.8, 1.95, (void *)font, "press a for All");
renderBitmapString(-0.8, 1.8, (void *)font, "press n to stop");
}

To gain individual control of each of the scale, translate, rotate and color functions, we can separate the
parameter control into multiple functions.
void rotate(void){
Sleep(SLEEPMS);

12
angle += 5.0f;
// Resent if get out of window position
if (angle >= 360.0)
{
angle = 0.0f;
}
glutPostRedisplay();
}
void translate(void){
Sleep(SLEEPMS);
// xpos
xpos += 0.1f;
if (xpos >= 3.0f)
xpos = -3.0f;
// ypos
ypos -= 0.1f;
if (ypos <= -3.0f)
ypos = 3.0f;
glutPostRedisplay();
}
void changeColors(void){
Sleep(SLEEPMS);
R -= 0.01;
if (R <= 0.0f)
R = 1.0f;
G -= 0.02;
if (G <= 0.0f)
G = 1.0f;
B -= 0.03;
if (B <= 0.0f)
B = 1.0f;
glutPostRedisplay();
}
void scaleobj(void)
{
Sleep(SLEEPMS);
// scale factor
scale -= 0.01f;
if (scale <= 0.1f)
scale = 1.0f;
glutPostRedisplay();
}

We can also add a doAll() function which will perform all geometric transformations and color changes
simultaneously as we had implemented in the previous example:
void doAll(void)
{
Sleep(SLEEPMS);
// xpos

13
xpos += 0.1f;
if (xpos >= 3.0f)
xpos = -3.0f;
// ypos
ypos -= 0.1f;
if (ypos <= -3.0f)
ypos = 3.0f;
// scale factor
scale -= 0.01f;
if (scale <= 0.1f)
scale = 1.0f;
angle += 5.0f;
// Resent if get out of window position
if (angle >= 360.0)
{
angle = 0.0f;
}
R -= 0.01;
if (R <= 0.0f)
R = 1.0f;
G -= 0.02;
if (G <= 0.0f)
G = 1.0f;
B -= 0.03;
if (B <= 0.0f)
B = 1.0f;
glutPostRedisplay();
}

A keyboard function can be developed that matches the menu directions providing individual control of
each transformation and color change.
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'r':
// Rotate
glutIdleFunc(rotate);
break;
case 't':
// Translate
glutIdleFunc(translate);
break;
case 'c':
// Change colors
glutIdleFunc(changeColors);
break;
case 's':
// Scale
glutIdleFunc(scaleobj);
break;
case 'a':
// Do all

14
glutIdleFunc(doAll);
break;
case 'n':
// stop all
glutIdleFunc(NULL);
break;
default:
break;
}
}

Running the code found in CMSC405Week6_4.cpp results in the following graphic display. Following the
menu options allows a user to have more fine-tuned control over the sphere animation properties.

Finally, we can additional shapes to the scene to make it more interesting. When doing this, we can use
push/pop events glLoadIdentity() calls to control the animation on each shape. Adding additional 3D
shapes to resemble a drilling device is possible with the following code. After a wire sphere is added,
three additional 3D shapes using the glutWireCube(1.0) calls are added. Notice the matrix is
reloaded prior to adding the shapes to reset the matrix operations. Appropriate
translate, scale and rotate functions are used to create the appropriate placement and
subsequent animations. When building these scenes, graph paper will assist; however; some
trial and error for those fine tune adjustments might be needed.

15
// Display Function
void displayfcn() {
// Clear color and depth buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Model view matrix mode
glMatrixMode(GL_MODELVIEW);
// Reset the model-view matrix
// This is done each time you need to clear the matrix operations
// This is similar to glPopMatrix()
glLoadIdentity();
// Call Display Directions function
displayDirections();
// Colors controlled by tranform function
glColor3f(R, G, B);
// Since we are in ModelView we need to move to World coordinates
// xpos, ypos controlled by tranform function
glTranslatef(xpos, ypos, -7.0f);
// Scale controlled by tranform function
glScalef(scale, scale, scale);
// Rotate as dictated by angle parameter
glRotatef(sangle, 1.0f, 0.0f, 1.0f);
// Add a wireSpere mesh with 1.5 radius and 15x15 gridline
glutWireSphere(0.75, 15, 15);

// Add a wire cube


// Create a Left Wing
glLoadIdentity();
glTranslatef(-1.3, 0.0, -7.0);
glScalef(1.0, 0.2, 0.7);
glRotatef(langle, 1.0f, 0.0f, 0.0f);
glutWireCube(1.0);
// Create a Right Wing
glLoadIdentity();
glTranslatef(1.3, 0.0, -7.0);
glScalef(1.0, 0.2, 0.7);
glRotatef(angle, 1.0f, 0.0f, 0.0f);
glutWireCube(1.0);
// Create a Rudder
glLoadIdentity();
glTranslatef(1.3, 0.0, -7.0);
glScalef(0.1, 2.2, 0.7);
glRotatef(rangle, 0.0f, 0.0f, 1.0f);
glutWireCube(1.0);
// Double buffering
glutSwapBuffers();
}

16
Individual functions to control the rotation of each of the drill components is implemented with this
code:
void leftRotate(void){
Sleep(SLEEPMS);
langle += 5.0f;
// Resent if get out of window position
if (langle >= 360.0)
{
langle = 0.0f;
}
glutPostRedisplay();
}
void sphereRotate(void){
Sleep(SLEEPMS);
sangle += 5.0f;
// Resent if get out of window position
if (sangle >= 360.0)
{
sangle = 0.0f;
}
glutPostRedisplay();
}
// Rudder angle
void rudderRotate(void){
Sleep(SLEEPMS);
rangle += 5.0f;
// Resent if get out of window position
if (rangle >= 360.0)
{
rangle = 0.0f;
}
glutPostRedisplay();
}

Similar to the previous example, keyboard control is implemented using this function:
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 's':
// Rotate
glutIdleFunc(sphereRotate);
break;
case 'r':
// Translate
glutIdleFunc(rudderRotate);
break;
case 'l':
// Translate
glutIdleFunc(leftRotate);
break;
case 'c':
// Change colors
glutIdleFunc(changeColors);
break;

17
case 'a':
// Do all
glutIdleFunc(doAll);
break;
case 'n':
// stop all
glutIdleFunc(NULL);
break;
default:
break;
}
}

Running the code found in the file CMSC405Week6_5.cpp will result in the following graphic results:

You are encouraged to continue to experiment with the code, creating your own unique 3D animations.
It takes some to master the both the placement of the objects and the animation sequences. But with
time, you will be to create some impressive 3D animations.

Você também pode gostar