Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

This article, intended for developers, will explain in detail the POV-Ray code necessary to generate the dodecahedron appearing in our third gallery image (Duality). We will then show how to create an animation based on this scene. This, and the recommended references, should provide a solid foundation for those interested in experimenting with the use of ray tracers for mathematical exposition and exploration.

  1. Obtaining and Installing POV-ray
  2. Rendering Your First Scene
  3. The POV-Ray Scene Description Language
  4. Creating the Dodecahedron
  5. Animating the Scene
  6. Conclusion
  7. References
  8. POV-Ray Related Links
  9. Footnote for Mac Users

The companion article, A Gallery of Ray Tracing for Geometers, introduced ray tracing as a tool for mathematical exposition.

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

This article, intended for developers, will explain in detail the POV-Ray code necessary to generate the dodecahedron appearing in our third gallery image (Duality). We will then show how to create an animation based on this scene. This, and the recommended references, should provide a solid foundation for those interested in experimenting with the use of ray tracers for mathematical exposition and exploration.

  1. Obtaining and Installing POV-ray
  2. Rendering Your First Scene
  3. The POV-Ray Scene Description Language
  4. Creating the Dodecahedron
  5. Animating the Scene
  6. Conclusion
  7. References
  8. POV-Ray Related Links
  9. Footnote for Mac Users

The companion article, A Gallery of Ray Tracing for Geometers, introduced ray tracing as a tool for mathematical exposition.

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

1. Obtaining and Installing POV-Ray

POV-Ray is distributed in compiled format for Macintosh, Windows and Linux. Binaries, source code and installation instructions for these three platforms are available at povray.org. Macintosh users may want to read the “Footnote for Mac Users” at the end of this article for more details.

Windows and Mac users have a graphical user interface. The POV-Ray scene description file is composed in an editor window and the scene it describes is rendered by pressing a button. In Linux, POV-Ray is controlled via the command line. My favorite development environment consists of using the “Kate” editor and typing commands in the shell window that comes attached.

2. Rendering Your First Scene

For most of this tutorial, instructions are given from the perspective of a Linux user. Since slightly different instructions apply for Windows and Mac users these will be discussed immediately.

Download and open the following POV-Ray Scene Description file in your editor: sphere.pov.

Windows users should click on the Tools tab followed by the “Edit resolution INI file” menu item. The following text should be added at the end of the file:

[1280x720 AA 0.3]
Width=1280
Height=720
Antialias=On
Antialias_Threshold=0.3
Output_File_Type=N

Save this file in the usual way and restart POV-Ray. In the drop down text window on the upper left, select the resolution “[1280x720 AA 0.3]”. The scene file can now be rendered by pressing the “Run” button on the application toolbar.

Mac users of Mega-POV have a Preferences window available when the application is started. Under the Image Quality tab the image ratio should be set at 16/9 and the image dimensions at x=1280, y=720. The output image format should be set to “PNG”. The scene file can now be rendered by pressing the “Render” button.

Linux users (as well as Mac users who prefer the command line) should run the command

povray +P +I sphere.pov +W1280 +H720 +A

in a shell whose present working directory is the same as the pov file.

The pov file describes a green semi-transparent sphere centered at the origin, with three perpendicular axes and a square plate lying in the xy plane.

You should see a larger version of the following image (Figure 1):

test file -- sphere
Figure 1: Simple test file

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

3. The POV-Ray Scene Description Language

As we are about to see, the POV-Ray scene description language is well suited for creating mathematical images of all variety. For example, a dodecahedron is described as the union of 30 cylinders at certain locations with given surface properties. A mirror is described as a thin parallelepiped with 100% reflectivity. The viewpoint is given a precise location in 3-space, as are the light sources. And so on. Most of the thought process involved in creating a mathematical image can be characterized as visually-oriented mathematical thinking.

The scene in Figure 1 consists of a green semi-transparent sphere centered at the origin, with three perpendicular axes and a square plate lying in the xy plane. We will “begin at the beginning” in the next section, but for now let's see how this verbal description maps to the corresponding POV-Ray code:

A green semi-transparent sphere centered at the origin of radius 0.75:

sphere{ <0,0,0>, 0.75
   texture { 
      pigment{ color rgbt<0,1,0,0.5>}
      finish {diffuse .5 ambient .2 reflection .3 phong 0.5}
   } 
}

three gray perpendicular cylinders which represent our axes:

#declare axis = 
cylinder {<0,-3,0>,<0,3,0>,0.05
   texture{ 
      pigment{rgb<0.75,0.75,0.75>} 
      finish{ phong 1}
   }
}
object {
   axis
}
object {
   axis
   rotate< 0, 0, -90>
}
object {
   axis
   rotate<90, 0, 0>
}

a square, white, semi-transparent plate:

box {<-2, -2, -0.05>, <2, 2, 0> 
   texture {
      pigment{ color rgbt<0.9,1.0,0.9,0.5> }
      finish {diffuse .5 ambient .2 reflection .3 phong 1}
   }  
}

The more you work with POV-Ray, the more you are likely to find this mapping clear, efficient and effective.

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

4. Creating the Dodecahedron

We will create an image of a dodecahedron in four steps, introducing new elements as required. Our first image will simply show the background color. Then we will create images of one cylinder, five cylinders in the shape of a pentagon, and finally the fully assembled dodecahedron. To clarify the exposition, the new or modified code at each step will appear in bold type.

Background Color:

Cut and paste the following two lines into your editor. Save the file as dodec-01.pov.

// povray +P +I dodec-01.pov +W640 +H360 +A 
background{ color rgb<0.2, 0.2, 0.45> }

The first line is a comment containing the shell command for rendering the image. Here is what it means:

  • povray” is the name of the rendering engine

  • +P pauses the rendering window when rendering finishes instead of closing out this window

  • +I dodec-01.pov indicates which scene description file is to be used as input

  • +W640 +H360 give the width and height pixel dimensions of the resulting image

  • +A indicates that anti-aliasing is turned on

The second line indicates that the background of the scene will be dark blue. This is given by a red-green-blue 3-vector whose components can run from 0 to 1.

If you are using Windows or Mac GUI interface, you can simply click the “Run” button. If you are using Linux, you can cut and paste the first line (excluding the double slash which indicates a comment) into your shell. This first line is not required, but I find it a convenient way to reproduce the exact scene when running the code at a later time.

What you should see is a 640x360 dark blue scene.

Single Cylinder:

Our dodecahedron will be constructed using 30 cylinders, so let's start by creating an image with a single cylinder. Cut and paste the following code into your editor and save the file as dodec-02.pov. (Changes from dodec-01.pov above are indicated in boldface.)

// povray +P +I <strong>dodec-02.pov</strong> +W640 +H360 +A<br /><br />background{ color rgb<0.2, 0.2, 0.45> }<br /><br /><strong>camera {<br />        location <0, -8, 0><br />        up <0, 0, 1><br />        right <-1.78, 0, 0><br />        look_at <0, 0, 0><br />        angle 60<br />}<br /><br />light_source {<br />        <100, -100, 100><br />        color rgb<1, 1, 1><br />}<br /><br />cylinder {<0, 0, -1>,   <0, 0, 1>,  0.06<br />        texture{ <br />           pigment{rgb<1, 1, 1>} <br />        }<br />}</strong>

The rendered image should look like Figure 2:

single cylinder
Figure 2: A cylinder

In order to see the object, we need a camera and the object must be illuminated.

To describe the camera, we give it's location in 3-space, the direction of its “up” and “right” vectors, the direction it is looking and the viewing angle. A large viewing angle corresponds to a wide angle lens, allowing more of the scene to be rendered. The right vector might at first seem strange since it points in the negative x direction. I've done that because POV-Ray uses a left handed coordinate system by default. Specifying the right vector in this way transforms the coordinate system into the familiar right handed system that mathematicians and physicists love so well (positive z axis up, positive x axis to the right and positive y axis into the screen). The factor of 1.78 gives the relative width to height of the image: in this case, common 16:9 format seen in high definition video. Similarly, to define the light source we give its location in 3-space and its color vector (using red-green-blue components).

To verify that the coordinate system behaves as expected, let's add a translate directive to the description of the cylinder:

cylinder {<0,0,-2>,<0,0,2>,0.15
   texture{ 
      pigment{rgb<1,1,1>} 
   }
   <strong>translate<1,0,0></strong>
}

This translates the cylinder to the right along the x axis. Repeating by translating along the positive y and z axes give the expected results (Figure 3):

cylinder translation x

cylinder translation y

cylinder translation z
Figure 3: Translation

Adding Texture:

You can define the light scattering properties of a surface by using primitives that give specular reflectivity, diffuse reflectivity, transparency and so on. Or you can use one of the hundreds of predefined textures. To do that, a texture definition file must be included with your scene file. Save the following code as dodec-03.pov and render it (Figure 4). (Changes from dodec-02.pov above are indicated in boldface.)

// povray +P +I <strong>dodec-03.pov</strong> +W640 +H360 +A<br /><br /><strong>#include "golds.inc"</strong>
 
background{ color rgb<0.2,0.2,0.45>}
 
camera {
        location <0, -8, 0>
        up <0, 0, 1>
        right <-1.78, 0, 0>
        look_at <0, 0, 0>
        angle 60
        rotate <0, 0, 0>
}
 
light_source {
        <100,-100,100>
        color rgb<1,1,1>*1.5
}
 
<strong>cylinder {<0,0,-2>,   <0,0,2>,  0.15<br />        texture { T_Gold_5A }<br />}<br /></strong>

cylinder with texture
Figure 4: Changing the texture

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

A Pentagon:

We'll construct our dodecahedron from twelve congruent pentagons. Suitable coordinates for the 20 vertices of a dodecahedron centered at the origin are as follows:

\((\pm 1, \pm 1, \pm 1)\)
\((0, \pm 1/\phi, \pm\phi)\)
\((\pm 1/\phi, \pm\phi, 0)\)
\((\pm\phi, 0, \pm 1/\phi)\)

where \(\phi = (1+\sqrt5)/2\) is the golden ratio [3].

The POV-Ray scene description language is “Turing Complete”, which means it is capable of expressing any algorithm expressible by the familiar programming languages like C++ or Java. It has branching and looping constructs and allows the description of functions. Functions are most often defined in POV-Ray using the macro construct. We use the macro construct below to define a function called “Pentagon” which takes the coordinates of five points in space as input then constructs a pentagon as output. The union construct allows primitive objects to be gathered into a single compound object. The spheres are added at the endpoints to make the joints smooth. Two constants, phi and c = 1/phi are declared and used to make the code more readable. The code below creates one of the pentagons that form the final scene. Save it as dodec-04.pov and render it (Figure 5). (Changes from dodec-03.pov above are indicated in boldface.)

// povray +P +I <strong>dodec-04.pov</strong> +W640 +H360 +A
 
#include "golds.inc"
 
background{ color rgb<0.2,0.2,0.45>}
 
camera {
        location <0, -8, 0>
        up <0, 1, 0>
        right <-1.78, 0, 0>
        look_at <0, 0, 0>
        angle 60
        rotate <0, 0, 0>
}
 
light_source {
        <100,-100,100>
        color rgb<1,1,1>*2.0
}
 
<strong>#declare phi = (1 + sqrt(5)) / 2;
#declare c = 1 / phi;
 
#macro Pentagon(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, x5, y5, z5)
        union{
                cylinder {<x1, y1, z1>,   <x2, y2, z2>,  0.1}
                sphere {<x1, y1, z1>, 0.1}
                cylinder {<x2, y2, z2>,  <x3, y3, z3>, 0.1}
                sphere {<x2, y2, z2>, 0.1}
                cylinder { <x3, y3, z3>, <x4, y4, z4>, 0.1}    
                sphere {<x3, y3, z3>, 0.1}          
                cylinder { <x4, y4, z4>, <x5, y5, z5>, 0.1}
                sphere {<x4, y4, z4>, 0.1}        
                cylinder { <x5, y5, z5>, <x1, y1, z1>, 0.1}
                sphere {<x5, y5, z5>, 0.1}
        }            
#end
 
object {
        Pentagon( 0, -phi, c, 0, -phi, -c, 1, -1, -1, phi, -c, 0, 1, -1,  1)
        texture { T_Gold_5A }
        scale 1.25
}  <br /></strong>

pentagon with texture
Figure 5: A pentagon constructed from five cylinders

The Final Scene:

The only thing left is to add the remaining 11 pentagons. Note that we have declared a dodecahedron to be the union of twelve pentagons. The code is given below -- save it as dodec-05.pov and render it (Figure 6). (Changes from dodec-04.pov are indicated in boldface.)

// povray +P +I <strong>dodec-05.pov</strong> +W640 +H360 +A<br /><br />#include "golds.inc"<br /><br />background{ color rgb<0.2,0.2,0.45>}<br /><br />camera {<br />        location <0, -8, 0><br />        up <0, 1, 0><br />        right <-1.78, 0, 0><br />        look_at <0, 0, 0><br />        angle 60<br />        rotate <0, 0, 0><br />}<br /><br />light_source {<br />        <100,-100,100><br />        color rgb<1,1,1>*2.0<br />}<br /><br />#declare phi = (1 + sqrt(5)) / 2;<br />#declare c = 1 / phi;<br /><br />
#macro Pentagon(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, x5, y5, z5)
        union{
                cylinder {<x1, y1, z1>,   <x2, y2, z2>,  0.1}
                sphere {<x1, y1, z1>, 0.1}
                cylinder {<x2, y2, z2>,  <x3, y3, z3>, 0.1}
                sphere {<x2, y2, z2>, 0.1}
                cylinder { <x3, y3, z3>, <x4, y4, z4>, 0.1}    
                sphere {<x3, y3, z3>, 0.1}          
                cylinder { <x4, y4, z4>, <x5, y5, z5>, 0.1}
                sphere {<x4, y4, z4>, 0.1}        
                cylinder { <x5, y5, z5>, <x1, y1, z1>, 0.1}
                sphere {<x5, y5, z5>, 0.1}
        }            
#end<br /><br /><strong>#declare dodecahedron = <br />union {<br />        Pentagon( c, 0,  phi, -c, 0, phi,  -1,  1,  1, 0,  phi,  c,  1,  1,  1)<br />        Pentagon(-c, 0,  phi,  c,  0, phi,  1, -1,  1, 0, -phi,  c, -1, -1,  1)<br />        Pentagon( c, 0, -phi, -c,  0, -phi,-1, -1, -1, 0, -phi, -c,  1, -1, -1)<br />        Pentagon(-c, 0, -phi,  c,  0, -phi, 1,  1, -1, 0,  phi, -c, -1,  1, -1)<br />        Pentagon( 0, phi, -c,  0,  phi,  c, 1,  1,  1, phi,  c,  0,  1,  1, -1)<br />        Pentagon( 0, phi,  c,  0,  phi, -c,-1,  1, -1,-phi,  c,  0, -1,  1,  1)<br />        Pentagon( 0,-phi, -c,  0, -phi,  c,-1, -1,  1,-phi, -c,  0, -1, -1, -1)<br />        Pentagon( 0,-phi,  c,  0, -phi, -c, 1, -1, -1, phi, -c,  0,  1, -1,  1)            <br />        Pentagon( phi, c,  0,  phi, -c,  0, 1, -1,  1, c,  0,   phi, 1,  1,  1)<br />        Pentagon( phi,-c,  0,  phi,  c,  0, 1,  1, -1, c,  0,  -phi, 1, -1, -1)<br />        Pentagon(-phi, c,  0, -phi, -c,  0,-1, -1, -1,-c,  0,  -phi,-1,  1, -1)<br />        Pentagon(-phi,-c,  0, -phi,  c,  0,-1,  1,  1,-c,  0,   phi,-1, -1,  1)<br />}<br /><br />object {<br />        dodecahedron<br />        texture { T_Gold_5A }       <br />        scale 1.25 <br />}<br /></strong>

dodecahedron
Figure 6: The Final Scene

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

5. Animating the Scene

The animation given in Part I of this paper has resolution 1280x720 pixels, and is 20 seconds long at 25 frames per second. This requires generating 500 high resolution frames of the object, each with a different rotation, and stitching the frames together. This process can take a significant amount of time, even on today's powerful desktop machines. In order to speed up the turn around time, we'll create a shorter animation at lower resolution. Let's start with a 10 second video at 320x180 resolution.

We can let POV-Ray automate frame generation by using a clock variable and indicating the start and end frames in the command line. The starting frame will be number 1 and the ending frame number 250. The clock variable starts at zero and increments 1/250 each frame. So the rotate command increments the rotation about the z axis 360/250 degrees between frames. Note that only two lines (the first and the last) have changed. Save this code as in a separate folder called “anim” since it directs POV-Ray to generate 250 images. Use the command given in the first line to render the 250 frames. By removing the +P switch, there will be no pause between frames as they are generated. By including the -D switch, the frames will not be displayed on the screen as they are generated, reducing overall computation time.

Mac users of Mega-POV can set the equivalent parameters under the “Clock Settings” tab in the Preferences window.

Windows users should click on the Tools tab followed by the “Edit resolution INI file” menu item. The following text should be added at the end of the file:

[320x180 anim, AA 0.3]
Width=320
Height=180
Antialias=On
Antialias_Threshold=0.3
Output_File_Type=N
Initial_Frame=1
Final_Frame=250

Save this file and restart POV-Ray. In the drop down text window on the upper left, select the resolution “[320x180 anim, AA 0.3]”. Save it as animate.pov and render it. (Changes from dodec-05.pov above are indicated in boldface.)

// povray -D +I <strong>animate.pov</strong> +W320 +H180 <strong>Initial_Frame=1 Final_Frame=250</strong> +A
 
#include "golds.inc"
 
background{ color rgb<0.2,0.2,0.45>}
 
camera {
        location <0, -8, 0>
        up <0, 1, 0>
        right <-1.78, 0, 0>
        look_at <0, 0, 0>
        angle 60
        rotate <0, 0, 0>
}
 
light_source {
        <100,-100,100>
        color rgb<1,1,1>*2.0
}
 
#declare phi = (1 + sqrt(5)) / 2;
#declare c = 1 / phi;
 
#macro Pentagon(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, x5, y5, z5)
        union{
                cylinder {<x1, y1, z1>,   <x2, y2, z2>,  0.1}
                sphere {<x1, y1, z1>, 0.1}
                cylinder {<x2, y2, z2>,  <x3, y3, z3>, 0.1}
                sphere {<x2, y2, z2>, 0.1}
                cylinder { <x3, y3, z3>, <x4, y4, z4>, 0.1}    
                sphere {<x3, y3, z3>, 0.1}          
                cylinder { <x4, y4, z4>, <x5, y5, z5>, 0.1}
                sphere {<x4, y4, z4>, 0.1}        
                cylinder { <x5, y5, z5>, <x1, y1, z1>, 0.1}
                sphere {<x5, y5, z5>, 0.1}
        }            
#end
 
#declare dodecahedron = 
union {
        Pentagon( c, 0,  phi, -c, 0, phi,  -1,  1,  1, 0,  phi,  c,  1,  1,  1)
        Pentagon(-c, 0,  phi,  c,  0, phi,  1, -1,  1, 0, -phi,  c, -1, -1,  1)
        Pentagon( c, 0, -phi, -c,  0, -phi,-1, -1, -1, 0, -phi, -c,  1, -1, -1)
        Pentagon(-c, 0, -phi,  c,  0, -phi, 1,  1, -1, 0,  phi, -c, -1,  1, -1)
        Pentagon( 0, phi, -c,  0,  phi,  c, 1,  1,  1, phi,  c,  0,  1,  1, -1)
        Pentagon( 0, phi,  c,  0,  phi, -c,-1,  1, -1,-phi,  c,  0, -1,  1,  1)
        Pentagon( 0,-phi, -c,  0, -phi,  c,-1, -1,  1,-phi, -c,  0, -1, -1, -1)
        Pentagon( 0,-phi,  c,  0, -phi, -c, 1, -1, -1, phi, -c,  0,  1, -1,  1)
        Pentagon( phi, c,  0,  phi, -c,  0, 1, -1,  1, c,  0,   phi, 1,  1,  1)
        Pentagon( phi,-c,  0,  phi,  c,  0, 1,  1, -1, c,  0,  -phi, 1, -1, -1)
        Pentagon(-phi, c,  0, -phi, -c,  0,-1, -1, -1,-c,  0,  -phi,-1,  1, -1)
        Pentagon(-phi,-c,  0, -phi,  c,  0,-1,  1,  1,-c,  0,   phi,-1, -1,  1)
}
 
object {
        dodecahedron
        texture { T_Gold_5A }       
        scale 1.25
        <strong>rotate<0, 0, 360*clock> </strong>
}

After a few minutes, all the frames will have rendered. Notice that they are conveniently named animate001.png through animate250.png. If you have ImageMagick installed on your computer, you can immediately watch the animation using the command

animate *.png

To generate a movie file in one of the usual formats, I strongly recommend using the open source audio/visual manipulation tool FFMpeg. It can be downloaded at ffmpeg.org The command for generating a video in quicktime format is

ffmpeg -sameq -r 25 -i animate%03d.png animate.mov

This directs ffmpeg to generate a movie in quicktime format (animate.mov) at the same quality as the images and at a rate of 25 frames per second (click on Figure 7 to run the animation).

dodecahedron with linked animation
Figure 7 with linked animation: Animating the scene

Creating Photo-realistic Images and Animations

Author(s): 
Michael Grady (Southern Utah Univ.)

6. Conclusion

Once a scene has been created in a ray tracer, there are an infinite variety of possible viewpoints and transformations to try out. There is not just one image, but a virtual world that can be explored via animation. How many of us have built or are willing to undertake the painstaking effort of building a trihedral kaleidoscope? With a ray tracer, you can construct one virtually and experience the extraordinary effect of seeing a truncated dodecahedron formed in space from the reflections of a single cylinder. This is an excellent way to develop the mathematical imagination.

We have just barely scratched the surface of what is possible for the imaginative expositor, student or explorer of mathematics who uses a ray tracer. A look at Coxeter's beautiful book: Introduction to Geometry [1] or Needham's remarkable text Visual Complex Analysis [2] should suggest a wealth of fascinating new experiments.

7. References

[1] Coxeter, H.S.M., Introduction to Geometry, 2nd edition (1989: Wiley).

[2] Needham, Tristan, Visual Complex Analysis (1997: Oxford Univ. Press), p.140-142 http://www.usfca.edu/vca/.

[3] Wikipedia contributors, "Dodecahedron," Wikipedia, The Free Encyclopedia (October 2009) http://en.wikipedia.org/wiki/Dodecahedron.

8. POV-Ray Related Links:

  1. Wikipedia article on POV-Ray: en.wikipedia.org/wiki/POV-Ray
  2. Official POV-Ray Tutorial: www.povray.org/download
  3. Official POV-Ray Reference Manual: www.povray.org/download
  4. Lohmueller has a set of excellent tutorials illustrating the use of POV-Ray to teach Analytic Geometry: http://www.f-lohmueller.de/pov_tut/a_geo/a_geo__e.htm

9. Footnote for Mac Users

The Mac version of POV-Ray at povray.org was compiled in 2004 for the PowerPC. If your system is Intel based and you encounter difficulties running the package, there are two options. The first is to use Mega-POV instead (megapov.inetart.net). This is a custom and unofficial patched version of POV-Ray with a graphical editor and many useful extensions. The other option, recommended for those who prefer the command line, is to download the Unix source and compile it yourself.