8.1 - Introduction to Animations¶
Hopefully the previous two chapters have taught you how to transform models and cameras to create a rendering of a virtual scene. Now we need to discuss how to make models and cameras change over time to create animations.
An animation is a series of renderings over a time period. An animation has a frame rate which determines the number of renderings per second. The human eye has a characteristic called persistence of vision which allows a person to see smooth motion of objects over time – assuming an animation’s frame rate is of a sufficient speed. The required frame rate varies based on the lighting conditions of the environment where the animation is viewed. In a dark environment, such as a movie theatre, 24 frames per second is typically sufficient to perceive smooth motion. In environments with more light, such as your home’s family room, TV’s need 30 frames per second to produce smooth motion. Video with higher frame rates, called HFR, have recently been produced, such as Peter Jackson’s The Hobbit film series.
To display an animation each frame is illuminated for a short period of time. If the time between frames is not sufficiently small, some people will see a flicker of light between frames. To reduce “flicker,” the typical TV image is refreshed 60 times per second. In a movie theatre, “flicker” is reduced by displaying each image of a film twice, which means the display frame-rate of the movie is 48 frames per second, even though there are only 24 unique images per second.
Key Frames¶
The fundamental concept used to create animations is called key framing. To use key framing you assign a starting and ending position, orientation, and/or size to a camera or a model for a specific range of frames. Then the computer calculates the intermediate position, orientation, and/or size for the intermediate frames. The easiest way to interpolate values between the starting and ending values based on a frame number is “parametric equations” – which we will discuss in detail in the next lesson.
In Summary
An animation is produced by rendering a series of images at a specific frame rate, where the objects and/or the camera has changed over time.
Let’s discuss how a computer actually creates a series of scene renderings that a viewer perceives as a smooth motion animation.
Double Buffering¶
If rendering software were to write changes directly to a visible canvas on the screen, a viewer would be able to see incremental changes in the image which would distract from the animation. Therefore, WebGL automatically implements double buffering, which always renders graphics into an offscreen frame buffer. What is visible on the screen is a “second” onscreen frame buffer – thus the term “double” buffering. Rendering is done “offscreen” so that the changing of individual pixels is never visible to a user. The entire offscreen frame buffer can be copied to the onscreen frame buffer very quickly – in less than one screen refresh cycle. Therefore, a user never sees the actual rendering of a frame, only the final results of a rendering.
Updating the Screen¶
The screen of your computer monitor is composed of pixels which are small dots of phosphor that give off light when they are energized. The phosphor illuminates for a very short period of time and must be re-energized many times per second to produce a continuous image. A monitor’s refresh rate is the number of times it re-energized the screen’s pixels per second. The typical refresh rate is 60 times per second, but some newer monitors update at higher frequencies.
When WebGL recognizes that a new rendering has been completed in its offscreen frame buffer, it copies the contents of the offscreen buffer to the screen’s visible frame buffer. It only performs this copy when the video memory is not busy with a screen refresh. Therefore, if a screen is being updated 60 times per second, there are 60 possible time spans between screen refreshes when the visible buffer can be updated. If your WebGL program can render a new image in less than 1/60th of a second, then your program can be based on a 60 frames/second frame rate. If your rendering process takes longer than 1/60th of a second, then your program will only be able to update the screen every other refresh cycle, which would give you 30 frames per second. If your render processing takes more that 2/60th of a second, then your program will only be able to update the screen every 3rd refresh cycle. Hopefully you see a pattern. A WebGL program is typically designed to animate at a particular frame rate and that frame rate needs to be a multiple of the monitor’s refresh rate. For a 60 Hz screen, the possible frame rates are 60, 30, 20, 15, 10, 6, 5, 4, 3, 2, and 1.
The requestAnimationFrame
Function¶
You can create WebGL animations using JavaScript timer events, but timer events
were not designed for animations. For example, what if an animation is
running in a browser tab and the tab gets covered up by another tab or
another application window. A JavaScript timer event will continue to fire
and perform lots of computations for a rendering that can’t be seen! Therefore
JavaScript introduced a function specifically for animations called
requestAnimationFrame(myFunction)
. This function requests that a specific
function be called before the next onscreen buffer refresh, with the
caveat that nothing is done if the WebGL canvas is not visible.
The typical animation function performs the following tasks:
- Calculate the amount of elapsed time since the last frame rendering.
- If it is time to render a new frame:
- Change appropriate scene variables
- Render the scene
- Call
requestAnimationFrame
to continue rendering at a future time.
Here is an example animation function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | let previous_time = 0;
let frame_rate = 30; // frames per second
let milliseconds_between_frames = Math.round( (1 / frame_rate) * 1000 );
/**----------------------------------------------------------------------
* Animate a scene.
*/
self.animate = function () {
let now, elapsed_time;
if (scene.animate_active) {
now = Date.now();
elapsed_time = now - previous_time;
if (elapsed_time >= milliseconds_between_frames) {
// Remember when this scene was rendered.
previous_time = now;
// Change the scene
self.t += dt;
// Update the screen
scene.render();
}
window.requestAnimationFrame(self.animate);
}
};
|
Please make the following observations about this code:
- The
requestAnimationFrame
sets the callback to the same function it is in. - The
previous_time
variable is declared outside this function so it can retain its value from one function call to the next. - There must be some mechanism for stopping an animation. This code uses
a value from the scene object called
animate_active
. The animation can be stopped by a separate event handler setting this variable tofalse
. - Accurate timing requires that you track time from the start of the rendering
of one frame to the start of the next frame. Notice that
Date.now()
is called only once and saved in a local variable. That local variable is used to update theprevious_time
value if a rendering is performed. Only callDate.now()
once. - The variable
milliseconds_between_frames
is calculated from the frame rate. It must be an integer value – thus theMath.round()
function call. The JavaScript functionDate.now()
returns integer time values in millisecond. - The browser will call
requestAnimationFrame
once per computer monitor refresh. For a computer’s monitor whose refresh rate is 60 times per second, the calls will be 16 or 17 milliseconds apart. The timing is not exact due to round-off errors in millisecond precision.
Here is a visual diagram of the concepts we have discussed:
WebGL Example Program¶
Experiment with the following program. You can change the starting and
ending values for the path, along with the starting and ending animation
frame by editing lines 74-76 of animate_scene.js
.
The _animate()
function is in lines 171-193 of animate_events.js
.
You can change the frame rate in line 50 of animate_events.js
.
Animate an object along a path between two points.
current frame 0 : 0 120
animation: start frame: end frame:
Glossary¶
- frame
- One image, of a series of images, that makes up an animation or video.
- frame rate
- The number of frames per second in an animation.
- refresh rate
- The number of times per second a computer monitor refreshes the color of each pixel on its screen. The color values come from a “frame buffer” in video memory.
- Hz
- Hertz; cycles per second; a unit of measurement used to specify refresh rates.
- persistence of vision
- A characteristic of the human visual system (eye and brain) that perceives smooth motion of an object from a video or animation.
- double buffering
- The use of two frame buffers: one offscreen frame buffer to render into, and another onscreen frame buffer that holds what is visible to the user.
- offscreen frame buffer
- A rectangular, 2D array of pixel values that holds a rendering.
- onscreen frame buffer
- A rectangular, 2D array of pixel values that holds an image. A computer monitor is refreshed from its onscreen frame buffer.
- key framing
- Calculate the properties of an object (or camera) at intermediate frames based on values set at a starting and ending frame.
Self Assessment¶
-
Q-453: What property of the human visual system (eye and brain) allows a human
to perceive smooth motion from a series of discrete images displayed in rapid succession?
- persistence of vision
- Correct.
- flicker
- Incorrect. Flicker is due to the rapid decrease in intensity of screen phosphors.
- frame rate
- Incorrect. Frame rate is the number of discrete images that are displayed per second for an animation.
- key framing
- Incorrect. A key frame is a frame where the properties of an object have been specified by an animator.
-
Q-454: The frame rate of an animation is specified in what units?
- frames per second
- Correct.
- time per frame
- Incorrect.
- distance moved per frame
- Incorrect.
- frames per minute
- Incorrect.
-
Q-455: Double buffering is implemented to do what?
- Prevent the user from seeing partial changes to an animation frame.
- Correct. Only after an entire rendering has been completed does it become visible to a user.
- Allow a rendered image to be post-processed.
- Incorrect.
- Keep GPU rendering processes separate from JavaScript commands.
- Incorrect.
- Prevent the corruption of a rendering from JavaScript commands.
- Incorrect.
-
Q-456: If a WebGL program is executing on a system whose monitor is being
refreshed 30 times per second, which of the following are valid
refresh rates for an animation? (Select all that apply.)
- 30
- Correct. This would render a new image for each refresh cycle of the monitor.
- 15
- Correct. This would render a new image for every other refresh cycle of the monitor.
- 12
- Incorrect. 12 is not evenly divisible into 30, so there is no way to update the frames on a uniform time interval.
- 20
- Incorrect. 20 is not evenly divisible into 30, so there is no way to update the frames on a uniform time interval.
-
Q-457: The
- When the canvas being rendered into is not visible on the screen.
- Correct. This prevents wasted computation on a rendering that is not visible.
- When you program's "animation is active" flag is set to false.
- Incorrect.
- When an event handler calls it.
- Incorrect.
- When an animation's frame counter is set to its maximum value.
- Incorrect.
requestAnimationFrame()
function does nothing under
what special circumstances?