OpenGL Animation
-
Upload
richard-mcdaniel -
Category
Documents
-
view
47 -
download
9
description
Transcript of OpenGL Animation
-
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.
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.
0
0.2
0.4
0.6
0.8
1
1.2
abs(sin(x))
damped k=0.4
damped k=0.8
-
3
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()
0
0.2
0.4
0.6
0.8
1
1.2
abs(sin(x))
damped k=0.4
damped k=0.8
-
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.
-
6
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
-
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
-
10
if (xpos >= 3.0f) xpos = -3.0f; // ypos ypos -= 0.1f; if (ypos
-
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
-
13
xpos += 0.1f; if (xpos >= 3.0f) xpos = -3.0f; // ypos ypos -= 0.1f; if (ypos
-
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.