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.

Side note:

OpenGL, from which WebGL is derived, does not perform double buffering automatically. In OpenGL a programmer must enable and manage double buffering using calls to the application’s window manager software (typically GLUT or FreeGLUT).

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 to false.
  • 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 the previous_time value if a rendering is performed. Only call Date.now() once.
  • The variable milliseconds_between_frames is calculated from the frame rate. It must be an integer value – thus the Math.round() function call. The JavaScript function Date.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:

../_images/frame_buffers.png

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.

Show: Code   Canvas   Run Info
../_static/08_animation/animate.html

Animate an object along a path between two points.

Please use a browser that supports "canvas"

Timing:
current frame 0 : 0 120
animation: start frame: end frame:

Show: Process information    Warnings    Errors
Open this webgl program in a new tab or window

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 requestAnimationFrame() function does nothing under what special circumstances?
  • 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.
Next Section - 8.2 - Parametric Equations