Animation involves rendering a sequence of frames that are then assembled into a format suitable for animated display. The generation of each frame from one or more scene files can be performed using POV-Ray. Assembly needs to be performed using a separate tool. Various tools are available to assemble individual frames generated by POV-Ray as BMP or PNG files into an animated format (e.g. animated GIF, MOV or MPEG).

Various approaches can be used with POV-Ray to generate frames for animation. This documentation only covers the technique used by the POV-Person library.

POV-Person helps to shortcut the normal processes of animation by using a small number of manually defined key-poses and interpolating between them. POV-Person also incorporates macros to provide position mirroring to further reduce the number of poses that need to be manually defined. As a consequence, the standard 30 frame animation loop of a person walking has been constructed using four manually defined poses.

POV-Person includes various pre-defined sequences and these sequences can be chained together and smoothed using POV-Person macros to enable more complex sequences to be developed. Furthermore, interpolated frames can be written to disk enabling them to be used as key-poses for other animations.

Overview

To get an understanding of how the animation capabilities of POV-Person work, we'll look at the walking files supplied with POV-Person. The file ppwalking.inc is a POV-Person position file that you can use from your POV-Ray scene file by making the following macro call: #declare person = ppFigure("ppann.inc","ppstyle4.inc","smile","ppwalking.inc","ppoutfit2.inc");

Using the Ann figure with style4 (no hair) gives you a figure that is quite quick to render.
Outfit2 adds a simple costume, alternatively specifying "none" for the costume will render slightly quicker.

The ppwalking.inc file uses the POV-Ray clock variable to control the animation sequence. If you use it in a normal static scene file, the clock variable will default to 0 which will generate the first position from the walking sequence.

To animate a scene you can add the +KFF command line option followed by a value for the number of frames to generate.

For example +KFF6 will generate 6 frames and will cycle the clock variable from 0 to 1 as it does so. This means that when the first frame is generated the clock variable will have a value of 0, for the second frame it will have a value of 0.2, and for the third through sixth frames the clock variable will have the values 0.4, 0.6, 0.8 and 1.

The figure on the right shows how to set the command line option when using the Windows version of POV-Ray.

The ppwalking.inc file uses a set of 8 files that define the key poses. These files are ppwalking0.inc through ppwalking7.inc. The first 4 files contain joint rotation settings. The last 4 files use the first 4 files and then mirror those joint rotation settings.

If you open ppwalking.inc in a text editor you will notice that it starts with a little bit of SDL that allows you to render a scene from the include file (The 8 include files storing the key poses contain something similar). Ignore this for now and look for the following lines of SDL:

#local ppKeyFrameCount = 8;
#local ppWalkTimings = array[ppKeyFrameCount+1] {0,0.2,0.3,0.4,0.5,0.7,0.8,0.9,1};

The ppWalkTimings array effectively stores the timing of the 8 key poses. Notice that these don't have to be at regular intervals, so the interval between the first key pose (as stored in ppwalking0.inc) and the second key pose (as stored in ppwalking1.inc) will be 0.2 clock cycles, whereas the interval between the second and third key poses is only 0.1 clock cycles. Notice also that there are 9 timings specified. Because our walking loop will repeat once per full clock cycle, the last time boundary (1 clock cycle) will map to the first time boundary (0 clock cycles).

The next section of SDL works out which pose files to use for this image frame based upon the value of the clock. In fact we use our own version of the clock setting called ppClock that needs to be defined in the main scene file. In this case if you look at the file pp00animationexample01.pov you'll see that the ppClock variable is defined as being clock*ppFrameCount/(ppFrameCount+1) where ppFrameCount is the number of frames that will be rendered.

Adjusting the clock

To understand why we need to do this, think of using the clock variable to represent the passage of 1 second of time as it passes from 0 to 1. If we want to generate a repeating animation cycle of 1 second, then the frame generated for a clock setting of 0 needs to be the same as the frame generated for a clock setting of 1. So if we generate 6 frames with the first at 0 seconds into the animation and the last at 1 second into the animation, the first and last frames would represent the same moment in the cycle and would therefore be superfluous. By adjusting our copy of the clock setting into ppClock we can avoid this problem, so, to generate 5 frames we multiply clock*5/6 which means that as clock runs from 0 to 1 in steps of 0.25 seconds, ppClock runs from 0 to 0.8 in steps of 0.2 seconds. When we come to generate the animation file, the sequence of frames should then run smoothly from the end of one cycle into the start of the next.

In ppwalking.inc, we use a #while loop to step through the timings recorded in the ppWalkTimings array using ppClock variable to work out which pose files we need to use to generate the current frame. The first pose file is included. The ppStashJointRotations macro is used to store the joint rotation settings away into a safe place. The second pose file is then included and the ppInterpolatePose macro is called to interpolate between the saved set of joint positions and the current set of joint positions, overwriting the current joint positions as it goes. The parameter to the ppInterpolatePose macro tells it how far between the first and second pose file to interpolate.

#while (ppClock>=ppWalkTimings[ppKeyFrame2])
#local ppKeyFrame1 = ppKeyFrame1+1;
#local ppKeyFrame2 = ppKeyFrame2+1;
#end

#local ppArrayIndex1 = ppKeyFrame1;
#local ppArrayIndex2 = ppKeyFrame2;

#if (ppKeyFrame2>ppKeyFrameCount-1) #local ppKeyFrame2 = 0; #end

#include concat("ppwalking",str(ppKeyFrame1,1,0),".inc")
ppStashJointRotations()
#include concat("ppwalking",str(ppKeyFrame2,1,0),".inc")

ppInterpolatePose((ppClock-ppWalkTimings[ppArrayIndex1])/(ppWalkTimings[ppArrayIndex2]-ppWalkTimings[ppArrayIndex1]))

For example, if the ppClock variable happens to contain the value 0.32, then we use pose frames ppwalking2.inc and ppwalking3.inc (remember to start counting from 0 not 1). The interpolation parameter tells the ppInterpolatePose macro to use joint rotations that are (0.32-0.3)/(0.4-0.3) = 0.2 or 20% of the way between those specified in ppwalking2.inc and those specified in ppwalking3.inc.

If you look at ppwalking2.inc (ignoring the little bit at the top for now) you will see a standard pose file with a series of joint rotations being specified using the ppRotateJoint macro. For example:

ppRotateJoint(ppLeftShoulderJointRef , 0, 20, 6) specifies that the left shoulder joint should be:

If you look at ppwalking3.inc you'll see

ppRotateJoint(ppLeftShoulderJointRef , 0, 15, 6) Therefore the ppInterpolatePose macro would evaluate to the equivalent of specifying ppRotateJoint(ppLeftShoulderJointRef , 0, 19, 6)

Fixing the location and orientation of a body part

Finally it is necessary to record the location and orientation of a single body part, from which POV-Person will calculate the position and orientation of the rest of the figure. For the 'walking' sequence we use the base of the foot. However, the fixed body part will change during the cycle, in this case, from the left foot to the right foot and back during the course of the full animation cycle. Each position file records the body part reference, position and orientation used to define its pose using the ppFixBodyPart macro, so position files 0, 1 and 2 use the following macro call: ppFixBodyPart(ppLeftFootRef, <0,0,0>, <0,0,0>) This positions the left foot at the origin with no rotation.

The pose defined in ppwalking2.inc brings the right foot in contact with the floor in preparation for the left foot to be lifted. Therefore we need to find the position of the right foot from ppwalking2.inc and use it to fix the position of the right foot in ppwalking3.inc. This information is therefore written out to the message stream using the #debug instruction. The following information should be similar to that output by ppwalking2.inc

This tells us that the right foot is 0.057 units (5.7cm) to the right of and 0.53 units (53cm) in front of the left foot. It also tells us that the height is slightly different (about 1mm). Ideally this should be in the same horizontal plane for the walking sequence, but this is close enough. Similarly, in an ideal world the orientation would be 0, but the model has been adjusted so that the foot rotation is less than one degree out of alignment in each of the axes, which, once again is close enough. The settings we use for ppwalking3.inc are therefore: ppFixBodyPart(ppRightFootRef, <0.057,0,0.53>, <0,0,0>) When we get to ppwalking6.inc we have the reverse situation where the left foot has reached the floor and in ppwalking7.inc we lock the left foot position using: ppFixBodyPart(ppRightFootRef, <0,0,1.06>, <0,0,0>)

With this it is possible to generate animations by defining a complete sequence of key positions and a file to control interpolation. However, even for the more artistic amongst you it can be difficult to visualize the combination of joint rotations and transitions required to generate smooth animations. POV-Person therefore includes a number of small utilities to help design and tailor your animations.

The Mirror Macro

The ppMirror macro is used to mirror some of the main joints from left to right and from right to left. This is useful for cyclic animations such as the 'walking' animation, where the second half of a walk cycle can be defined as a mirror image of the first half.

If you look at ppwalking04.inc you will see that it mirrors the first key pose in the sequence with the statement:

ppMirror("ppwalking0.inc") Key poses 5 through 7 do the same with poses 1 through 3.

The Animation Testbed

At the top of each of the position files for the 'walking' animation is a short sequence of SDL that enables a position file to be rendered. A set of variables are set to specify the pose or poses to be shown then pp00animationtestbed.pov is included to render a scene. The animation testbed scene file uses the variables to determine which pose files to include. The following settings are typical of those you would find in the ppwalking03.inc file. #ifndef(ppCamera) #declare ppAnimationName = "ppwalking";
#declare ppAnimationThisPose = 2;
#declare ppAnimationGhosts = 0.5;
#declare ppAnimationPose = array[10] {0,0,0,0,0,0,0,0,0,0};
#declare ppAnimationPose[1] = 1;
#declare ppAnimationPose[2] = 1;
#include "pp00animationtestbed.pov"

#else
...
#end
Where ppAnimationName is the name of the animation
ppAnimationThisPose is the number of this pose file
ppAnimationGhosts is a transparency setting to use with all but the current figure pose
the ppAnimationPose array is used to indicate which poses are included in the testbed scene
In the example above, the poses defined in ppwalking1.inc and ppwalking2.inc would be included in the testbed scene. Because the current pose file is identified as ppwalking2.inc the figure posed using this file would be rendered using the standard skin textures whereas the figure posed using the settings from ppwalking1.inc would be rendered in a light grey, and because ppAnimationGhosts is set to 0.5 would also be given a transparency setting of 0.5.

The ability to 'overlay' different poses is very useful for lining up sequences of positions so that smooth transitions can be designed. The colouration of poses helps to avoid confusion about which body parts belong to the current pose. The transparency setting enables you to see the pose that you are working on, but considerably slows down the render. It is therefore recommended to set ppAnimationGhosts to '0' unless you find that another pose obscures detail that you need to see.

Further Reading on Character Animation

POV-Person only provides a few tools to assist with certain technical aspects of animation. Getting natural looking movement is the animators art.

There are various sites that discuss the more artistic aspects of animation including:
http://www.jdhancock.com/walking.asp
http://www.rubberbug.com/walking.htm
http://www.siggraph.org/education/materials/HyperGraph/animation/character_animation/walking/walking_excercise.htm