Frame-Rate Independent Animation using GLUT
|A triangle that rotates at constant speed regardless of CPU load.|
You may have experienced old computer games running so blazingly fast that they are unplayable, when run on a newer, faster, machine. This is due to the game relying on the timing of the specific machine for which it was designed. The basic GLUT template has the same problem. Try resizing the window. Make it small; notice how the triangle now spins at a ridiculously fast rate? Now make the window as big as the screen; the triangle slows down? This is because the different window sizes take different amounts of time to draw, resulting in different timing depending on the window size. The template simply uses 100% of the available CPU time to render and animate the triangle, with no regard to timing. This becomes even more annoying when another CPU intensive task is involved; the other task (or tasks) will cause the GLUT application timing to vary, resulting in stuttering animation. Thus, software such as games must be designed to compensate for variable timing. Methods for achieving this using GLUT are given below.
This template includes a project file for Visual Studio 2008 (i.e., Windows), and a makefile for Amiga OS 4.x+. Users of other systems should be able to modify the makefile for their systems easily.
Frame-Rate Limited Animation
The easiest method of obtaining constant-speed animation, is to limit the frame-rate via the use of a timer. GLUT offers a hook that calls a specified function after a desired delay. Thus, this can be achieved simply by changing the animate() function in the basic template to:
void animate(int value)
glutTimerFunc(TIMERMSECS, animate, 0);
... the rest of the animate code ...
Here, TIMERMSECS is the delay in milliseconds between calls to animate. The new parameter "value" is ignored, but could be used for a single call-back function to differentiate between different timer events. The only other change left is to replace the following in the main() function:
glutTimerFunc(TIMERMSECS, animate, 0);
This call starts the timer. This is all that is required for timer-based animation.
If you have a CPU monitoring utility, you will also note that this template uses a fraction of the CPU power of the other. This is because it only performs the processing required to render the animation at the desired speed. I recommend using a timer instead of an idle function (when in windowed mode) simply because there is no point in rendering 100+ fps on a display with a refresh rate of 60-75 Hz.
The link to the template source code can be found in the downloads section below.
Achieving Frame-Rate Independent Animation
The timer based template above does not completely solve the animation speed problem. It effectively sets a constant frame-rate. However, if one were to run other programs simultaneously with the timer-based GLUT template, the animation will still stutter. Likewise, if processing time for any operation within a GLUT application takes longer than the timer period, the animation speed would still change. This is because the timing is not guaranteed; the computer will take "best-efforts" to meet the timing, but overloading the CPU will make this impossible. What is required is a mechanism for measuring elapsed time (and/or the time since the last frame), and responding appropriately.
Fortunately, GLUT offers an easy way to obtain the elapsed time since the application was started via the following line of code:
int time = glutGet(GLUT_ELAPSED_TIME);
The animate template provides two variables in the animate method that can be used for animation timing. ElapsedTime gives the number of milliseconds since the first timer tick; timeSincePrevFrame is the time in milliseconds since the previous frame occurred. You may notice that the template has a startTime variable that is initialised with a glutGet(GLUT_ELAPSED_TIME); call. This is just me being cautious. It also offers the ability to reset the timer if you so wish (e.g., when a new game is started).
The template's default code rotates a triangle at constant angular velocity. This is achieved simply by multiplying the elapsed time with a constant, i.e.,:
rot = ROTRATE / 1000 * elapsedTime;
Try running this template simultaneously with the timer-based template, and then run other applications in order to bring CPU usage to 100%. Whilst the frame rate may drop and stutter, this template will keep the triangle rotating at constant speed, whereas the timer-based template will not. This shows frame-rate independent animation in action.
For more complicated animations, or game engines, it may be necessary to calculate the average frame-rate too. Often a game/animation engine will take varying lengths of time to perform all computations depending on factors such as the number of collisions, etc. In this case, one wishes to estimate what time it will be when the image is redrawn, not what time it is at the start of processing. However, the basic principles remain the same.
NOTE: For Amiga OS users this template requires features that are only available in MiniGL 2.0 or better.
Articles » Amiga OS 4 Articles » OpenGL/MiniGL Templates » Frame-Rate Independent Animation using GLUT