I mentioned last time that a big draw for me with POV-Ray is the ability to create three-dimensional scenes and move around them. Having lots of camera positions is part of that; I want to see my scene from multiple angles. (Moving about a 3D space was often a big part of what little interest I ever had in video games. I especially liked flying games.)
From the very beginning, knowing that POV has support for animation, I’ve wanted to take it to the next level and make 3D movies. Rather than frozen snapshots taken from a bunch of (hopefully) well-chosen points, I wanted a fluid movement through the space.
Today I thought I’d write about some tricks I use to do that.
Creating an animation involves all the creative decisions you make designing the scene, plus it involves all sorts of decisions you must make regarding the animation. Does the camera move its position? Does it change what it looks at (or how it looks at it)? Do any of the objects in the scene move?
One very simple form of animation is a “spinner” like the headline image of this article and the one on the left. The only thing animated is the object, which is set to rotate in place. In POV-Ray, this is extremely simple to set up.
When you tell POV to create an animation, it sets the
clock variable to a new value in each frame rendered. To make an object spin, just use the
clock variable to set the object’s rotation progressively in each frame. By default the clock’s value is the normalized range zero to one. For the object to rotate fully, it needs to move 360 degrees, so the actual code might look like this:
That will give the object a (single) full rotation. To insure a smooth rotation, be sure to set
Cyclic_Animation to “On” in the INI file that kicks off the animation. (I’ll get back to INI files in a bit.)
Creating an object where only certain parts move is a little more involved, but mostly follows the same pattern. If some of the moving parts rotate, they’re special cases of the example above. The angle may need to be set to not begin at zero and/or not end at 360. The general equation for a specific range is:
This gives you
adj_clk (adjusted clock) that runs from
end during the cycle of animation. You could turn it into a function:
You could also turn it into a generic “range” function by adding a third “current point” parameter and passing the
The adjusted clock values go in various
translate statements to move object parts as necessary. I go into this “range” idea in detail, because it’s crucial to what comes next.
Cinematography is not a subject I’m particularly qualified to write about, but I can show you the tricks I use to make moving and pointing the camera as easy as possible. As with the CAM.INI protocol, this is the culmination of previous versions that evolved to this.
So far I’m liking it a lot! The goal of any language or protocol is to be as expressive as possible, so that designers can focus on the design and not on the details of making the design. This protocol seems to do that pretty well considering that neither POV scene description nor macro language are particularly expressive languages.
The first step adds to what I showed you last time about how I pick a camera position. Leaving off the bit that insures
CameraIndex exists, the bit that sets the render camera looks like this:
studio_animation variable, if defined, puts the render into animation mode. (Which, as you see, disables the whole camera-selection protocol previously discussed; we’re not in Kansas anymore!)
studio_animation variable does more than just switch to animation mode. The first level of structure inside the
#else above is a
#switch statement with a
#case for each segment of the planned animation:
A “movie” (a long animation) consists of multiple animation segments. This is mainly a complexity management technique.In POV, you can have the
clockrun from any value to any value, so it’s possible to create a single long animation. But you still end up with a
#switchstructure to allow different kinds of movement. Each segment sees a different clock range, which requires a bit of normalization. I find it easier to just have separate animations (there are some output considerations that also make this a good idea).
Within each segment the camera can move, and independently its point of view can also change. My segments usually consist of a series of both in some combination. The camera might be moving forward while turning to look right and then back forward. That’s three sub-segments: turn right, look right, turn forward (all while moving forward).
Any distinct “phrase,” any movement with a distinct beginning and end, is a sub-segment. As the example above illustrates, there is a single camera “phrase” above (moving forward), but three point of view phrases. Animations tend to look better when these are very independent. I accomplish this with the next level of structure; what’s inside the
#switch statements are exactly what I meant about still needing to break down individual phrases of movement. No way ’round lots of
To understand what’s inside the
#switch statements, it’s necessary to understand that bit labeled important local vars. Each animation segment (each
#case like the one above), defines two sets of important variables: The camera positions within the segment, and the time slices that comprise the segment. They might look something like this:
Now what’s inside the those inner
#switch statements will make a bit more sense. (As for why one starts numbering at zero and the other at one, there is an explanation, but it’s lame and irrelevant anyway.)
Camera movements turn out to be very simple. Sometimes the camera is just standing in one spot for a sub-segment. Other times it’s moving from point ‘A’ to point ‘B’. The first inner
#switch statement, the camera movement one, might start looking like this:
Here we use
#range clauses in the
#switch to catch slices of clock time. Now you see the reason for the
time_N variables. They’re designed for the
#range clauses. You also see how the
adjclock() function normalizes the sub-segment. I’ll discuss the
MoveCAM() macro below.
Notice that the first sub-segment actually covers three sub-segments. This would be the case if, for example, the move to the door included a (1) turn away, (2) steady gaze away and (3) turn back sub-segments. (As you move to the door, you might look at something else for a moment and then turn back.)
What’s crucial is that each sub-segment set
cam_z to position the camera. The height of the camera,
cam_y, gets a default value (5 feet) way upstream, but any sub-segment can set it. That would be necessary to change the camera’s height, say if going up a flight of stairs.
The point of view portion is a bit more complex, but follows the same principle of using
#range clauses to break the segment into phrases of movement.
Most importantly, just as the camera position part did, it must set the point of view. Specifically, it must set the
pov_z variables. (Under the assumption that looking level is a common default,
pov_y is also set upstream to be identical to camera height.)
Because the camera variables are set first, it makes it very easy to set the point of view relative to the camera, either for a steady gaze or for a rotating gaze. The trickiest thing is shifting the point of view from one specific coordinate to another specific coordinate.
look_at attribute of the
camera object makes it easy to fix the camera’s point of view on a specific point. If you move the camera, it naturally turns to keep its gaze on that point. To shift smoothly from (or to) a specific POV, you need to calculate a series of POVs that take you smoothly from one to the other.
Which brings me to the
ShiftPOV macros. The first one is very simple. It’s just an extension of the “adjusted range” thing. There’s a start point and an end point, and an evenly distributed series of points between. It looks like this:
The second one is a bit more involved and involves trigonometry (which, contrary to popular belief, is both easy and fun (and useful))! It looks like this:
This one generates two vectors from the camera position to the two points of view, determines the horizontal angle between them and uses the clock to set the point of view accordingly (as a fraction of the total angle). It also adjusts the height of the POV smoothly from point to point.
Something to recognize is that these macros are highly coupled to animation code. For one thing, the first sets the camera position and the second sets the point of view (hence the use of
#declare). Also, the second one depends on knowing the camera position, since the view shift is relative to it! At the same time, if you use the same animation protocol consistently, you can re-use these macros.
To build a complex animation, break the whole into sensible segments, and break those into phrases (sub-segments) of individual movements. Use the tools above to move the camera and its point of view.
There is enough more to talk about, the ANI.INI file, what to do with the output “frames” and how I make MP4 movies, that I think this should be continued next time. (Also, I have a couple special announcements!)
Stay animated, my friends!