Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

Ray Tracing and the POV-Ray Toolkit


JUL94: Ray Tracing and the POV-Ray Toolkit

Ray Tracing and the POV-Ray Toolkit

A powerful multiplatform graphics package that comes with source code

Craig A. Lindley

Craig is a founder of Enhanced Data Technology (Colorado Springs, CO), developers of the imaging-database tool EnhancedView. He is the author of Practical Image Processing in C and Practical Ray Tracing in C, both published by John Wiley & Sons. Craig can be contacted at Enhanced Data or on CompuServe at 73552,3375.


Once, only graphics gurus with access to supercomputers had the compute power to master black arts such as ray tracing. Today's powerful PCs, combined with readily available ray-tracing software have changed this, making it possible for anyone with a PC to render almost anything. Even if you can't draw a straight line with a ruler, you can produce spectacular images. You only have to be able to visualize objects and their relationships in an imaginary three-dimensional setting.

The software I use for ray tracing is POV-Ray ("Persistence of Vision Ray Tracer"), a powerful, multiplatform package available free of charge in source-code and executable form. POV-Ray, which is written in C, is available for PCs (running under DOS, Windows, NT, or OS/2), Macintosh, Amiga, UNIX (including X Windows), and VMS workstations. POV-Ray (based on the DKBTrace 2.12 ray-tracer written by David Buck and Aaron Collins) is supported and maintained by the POV-Team, a group of volunteer programmers, designers, animators, and artists.

POV-Ray is not public-domain software. It is copyrighted by the POV-Team and used under the conditions set forth in its documentation. The POV-Ray package--documentation, executables, source code, and sample image files--is available on CompuServe's GRAPHDEV Forum, the PC Graphics area on America On-Line (jump keyword "PCGRAPHICS"), the Internet via anonymous ftp from alfred.ccs.carleton.ca (134.117.1.1), the "You Can Call Me Ray" BBS (708-358-5611), "The Graphics Alternative" BBS (510-524-2780), or any number of freeware/shareware disk houses. In addition, many of these sources have other tools designed for use with POV-Ray, including those for scene design (POVCAD), font generation, animation, automatic scene generation, and the like. Questions about POV-Ray should be directed to the POV-Team leader, Drew Wells (CompuServe, 73767,1244; AOL: Drew Wells).

POV-Ray provides a scene-description language, supports images of up to 4096x4096, and uses standard include files for predefined shapes, colors, and textures. POV-Ray can produce 24-bit color image files, fractal landscapes (using height fields), spotlights for sophisticated lighting, Phong and specular highlighting for realistic-looking surfaces, and several image-file output formats. It can render a wide range of shapes, including spheres, boxes, ellipsoids, cylinders, cones, triangles, planes, tori, hyperboloids, paraboloids, Bézier patches, height fields, blobs, quartics, and smooth Phong-shaded triangles. POV-Ray also supports constructive solid geometry (CSG) operations--unions, intersections, and differences--for combining primitive shapes into more complex ones. Finally, POV-Ray has built-in textures: marble, checkerboard, wood, bumpy, agate, clouds, granite, ripples, waves, leopard, wrinkled, mirror, chrome, brass, gold, silver, blue sky with clouds, sunset with clouds, sapphire agate, jade, shiny, brown agate, apocalypse, blood marble, glass, brown onion, pine wood, cherry wood, and more.

Ray-Tracing Fundamentals

Ray tracing produces photorealistic images--those in which the interplay of light and shadows with 3-D objects closely resembles that found in nature. An advantage ray tracing enjoys over other 3-D rendering techniques is the simplicity with which you can incorporate effects such as shadows, reflections, refractions, and transparency. The reason ray tracing can produce photorealistic images with these visual effects is because the tracing of rays simulates light. In other words, ray tracing models the interaction of light rays and objects using simple optical principles. [Editor's note: For details, see "Ray: A Ray Tracing Program in C++" elsewhere in this issue.] The rules used for light-ray and object interaction are simplified from those of nature but still provide realistic results. Most ray-tracing programs simplify nature by assuming that:

  • Light rays travel only in straight lines through homogeneous media.
  • Light interacts with objects only at their surfaces.
  • Properties of light such as diffraction, phase, polarization, wavelength, and attenuation over distance aren't generally taken into consideration.
Of course, the more complete a ray-tracing program is, the closer to reality are its optical models. The trade-off is computation time--the more accurate the model, the slower the program runs.

Ray tracing is an example of a class of graphics algorithms referred to as "point-sampling algorithms" which determine the visibility of a surface (object) using a finite number of sample points, making assumptions about the points in between. Ray tracing is thus an approximation of reality. (Other algorithms in this class are the Z-buffer, painter's, and scanline algorithms, all of which are used in hidden-surface determination and removal. A related class of algorithms, referred to as "continuous algorithms," tries to determine visibility continuously over entire surfaces.)

Performance limitations and aliasing problems arise as a result of sampling. As the number of points evaluated increases, so does the apparent realism of the image. Unfortunately, the computation time increases as well, so performance is an issue. The use of multiple light sources within a scene also increases realism, again at the cost of performance.

Aliasing is a problem which must be addressed whenever sampling is used. Aliasing arises from the inability to reconstruct the original signal from its discrete samples. The severity of the problem is directly related to the frequency of the signal being sampled and the sampling rate. If the sampling rate or frequency is high enough, aliasing is not a problem. However, as the sampling frequency nears that of the signal being sampled, undesirable low-frequency signal components called "aliases" are created.

When ray tracing is used to produce detailed images, some details are masked by aliases. This results in various types of image distortion, including "jaggies," which must be dealt with to produce photorealism. Aliasing artifacts in ray-traced still images are bad enough, but when animation of still images is required, temporal aliasing compounds the problem. Rendering an image with higher resolution (higher-frequency sampling) helps, but it doesn't solve the problem completely. You'll need to use antialiasing techniques such as those built into POV-Ray to mask the effects of aliasing.

To construct a ray-traced image, you begin by defining a 3-D scene of light source(s) and object(s) in relation to the position of the viewer. Each object is assigned a location and attributes that define its shape and surface properties (color, reflectivity, texture, and so on). With the scene defined, the view geometry specifies where in 3-D space the viewer's camera or eye will be located (where the scene will be viewed from), the direction the camera will be looking, the orientation of the camera, and the camera's field of view. The view of the 3-D scene is through a hypothetical window called the "view plane," which is mapped to the computer's monitor. No matter where the camera is located or how it is positioned, the view plane always lies between the camera and the objects in the 3-D scene.

At first, it may seem intuitive to trace light rays which begin at a light source, enter the scene, and--after bouncing through the scene--pierce the view plane and contact the eye. This approach, sometimes referred to as "forward ray tracing," is workable but computationally expensive. Accurately rendering a scene in this way would require billions of light rays to be generated and traced, and only a small percentage of these would have the correct trajectory to ultimately pass through to the eye. Such an image would require months of PC-computation time.

However, "ray casting" (also known as "backward" or "reverse" ray tracing) is much more efficient than forward ray tracing. Ray casting reverses the tracing of rays by starting at the eye location and generating a ray that passes through each pixel of the view plane on its way to the 3-D scene. As these rays strike objects in the scene, the pixel through which the ray passed takes on the color of the closest intersected object. If the point of intersection between the ray and the closest object has an unobscured view of a light source, the point of intersection is fully illuminated by the light source. If, however, any objects in the scene lie between the point of intersection and the light source, the object at that point is in shadow, and its corresponding pixel's intensity diminishes accordingly. Example 1 is a pseudocode description of this ray-tracing process.

Determining an object's color at the point of intersection with the eye ray would be easy if not for an object's reflective and/or refractive nature and texture. The color at the point of intersection is a function of the object's color, the light source's color, the distance from the light source, and any reflection/refraction. The calculation of color is a recursive process called "shading." Every time a reflective/refractive object is intersected by the eye ray, an additional set of rays must be generated. Each of the additional rays must then be traced back to each light source to determine its contribution to the object's color. Because the process is recursive, the resulting ray tree describes the components that contribute to the object's color. The color contributions at the leaves of the tree are evaluated and passed up the tree. Each contribution figures into the final color of the object and therefore into the color of the pixel with which it is associated.

The Image-Development Process

The image-development process is one of continual refinement, closely resembling the typical software-development process; see Figure 1. During the image-design stage, ideas are translated into scene-description language statements and entered into an ASCII image-model file. The image file is then read by the ray-tracer program, and a low-resolution image--which requires less time to generate--is produced. Next, the output file produced by the ray tracer is color quantized (if necessary) and displayed to see what it really looks like. Modifications are usually needed, a process that continues until the image meets expectations. At this point, I render the image in high resolution with antialiasing enabled and save it as a graphics file. This final image, which will probably be of much higher resolution, takes longer to produce, but only needs to be done once.

Once a ray-traced image is put into a standard, graphical file format (the PC version of POV-Ray produces TARGA), you can manipulate it, incorporate it into an animation sequence, or produce hard copy.

Using POV-Ray

Because ray tracing is computationally expensive, the more powerful your computer, the faster you'll get results. Minimum hardware for the PC is a 386 with 2 Mbytes of RAM. However, for complex imagery you'll want a math coprocessor if you want to see the results in your lifetime.

POV-Ray uses a scene-description language to implement ray-tracing concepts. Syntactically correct language statements are coded into an ASCII text file (with a .POV extension) that's read by the POV-Ray program for rendering. As you can see in Listing One , the general process of defining a ray-traced scene consists of:

  • Defining the camera (position and orientation). This describes where the camera is in the 3-D universe, which direction it is pointing, and which direction is up and to the right from the camera's perspective. Camera setup determines what the view of the resulting image will look at.
  • Defining the light source(s) (type, location, and color). This provides illumination for seeing any objects in the scene.
  • Defining the simple objects within the scene (floor, ocean, backdrop, and so on).
  • Combining simple objects into complex ones using composite objects and CSG.
  • Translating, rotating, and scaling objects, light sources, and camera.
  • Assigning colors, textures, and finishes for the objects in the scene.
Listing One is a basic scene-description file which generates the image in Figure 2. The visible portion of the scene consists of a red sphere with specular highlight casting a shadow on a blue floor-like plane. The language is block structured and, for the most part, generally understandable. Items in angled brackets (<>) are vectors used to describe location and direction to the ray tracer. Curly braces ({}) delineate object definitions, and double slashes (//) denote comments. Listing Two is the code which describes the starry black sky, checkered floor (including reflection), infinite-path sky, and other aspects of the complete ray-traced image in Figure 3. The complete source code that describes the image as well as the image itself are available electronically; see "Availability," page 3.

After using POV-Ray on projects ranging from book covers to greeting cards, I've come up with a number of hints and tips for speeding up the image-development process. For instance, one technique that saves time is to render the smallest practical image during image development. Small images are fine for establishing object and/or view-point placement; larger images are rendered when you need to see detail. Likewise, you can lower the POV-Ray rendering-quality factor (command-line option q) for less image-generation time. Faster rendering times are traded for lower-quality imagery, but lowering the quality during the image-development process can speed things up substantially.

You can also save time when developing complex scenes by rendering only the new portions of the scene. This is accomplished by commenting out previously verified portions of the image file which aren't needed to verify additions to the image. As the new parts are verified, they, too, can be commented out so that their rendering time does not contribute to image turn-around time. When all component parts of the image are verified, all comments can be removed and the complete image rendered.

Another way to save image-development time is to use antialiasing only when generating the final image. While antialiasing usually produces visually superior images, it does so at the expense of processing time. Not using it during image debugging or refinement saves a substantial amount of time.

Finally, you should make use of POV-Ray's DECLARE statement to define quantities used repetitively. For example, if an elaborate texture is to be used over and over, it should be declared once and referenced throughout the file.

Facts that need to be kept in mind concerning Pov-Ray include the following:

  • Positive x-axis is horizontal and to the right, positive y-axis is vertical and upward, and positive z-axis is into the scene.
  • The files shapes.dat, textures.dat, and colors.dat are usually included in each image file. They contain a basic set of definitions to make using POV-Ray easier.
  • Always declare light sources centered at the origin <0 0 0> and translate them into their final position.
  • Constructive solid geometry makes possible a union (a logical OR of the surfaces), an intersection (a logical AND), and a difference (the subtraction of the second surface from the first surface).
  • The optimized sphere shape cannot be scaled nonuniformly; scaling factors must be the same in the x-, y-, and z-dimensions. The exception to this is the quadric sphere.
  • All quadric shapes (except spheres and ellipsoids) are infinite in at least one direction. Scaling cannot be used to fit them into an image. They must be constrained using CSG to fit entirely within an image.
  • All shapes (except triangles) have an inside and an outside. A plane's outside is the side with the surface normal. The side of a plane opposite the normal is the inside. The concept of inside and outside is important when using CSG.
  • Composite objects are collections of simple objects that can be manipulated as a single object. Transformations applied to a composite object affect all simple objects contained within it.
  • The controllable surface-attribute parameters within POV-Ray are ambient, diffuse, brilliance, reflection, refraction, index of refraction (IOR), Phong, Phong size, specular, and roughness.
  • The sum of the ambient and diffuse parameters should never exceed 1.0.
  • A color map converts a number into a color using linear interpolation.
  • The color of an object must be specified within a texture block when rendering high-quality images.
  • A color with an alpha value of 1.0 is transparent; values approaching 0.0 are more opaque.
  • The use of bounding objects is recommended, as they can drastically decrease the image-generation time.
  • Transformations are always performed in relation to the origin. A sphere located at the origin will show no visible displacement when rotated. A sphere first translated away from the origin and then rotated will spin like a planet in orbit around the origin.
  • The "left-hand rule" is used to define the direction of rotation. When the thumb of the left hand is pointed in the positive direction of the axis being rotated around, the fingers will curl in the direction of positive rotation.
  • Use a right vector of <1.333 0 0> when rendering images in 320x200 256-color VGA mode. This compensates for the nonsquare pixels produced in this mode. The right vector can be changed to <1.0 0 0> when rendering in resolutions which have square pixels. For Super VGA, this means all 256 color modes above 640x400.
  • Two different types of textures exist within POV-Ray: coloration and surface-perturbation. Coloration textures include bozo, spotted, marble, wood, checker, checker_texture, granite, gradient, agate, and imagemap. Surface-perturbation textures include waves, ripples, dents, bumps, and wrinkles.
  • Textures produce approximately one feature change (color transition) across a sphere of radius one. Textures can be scaled to provide the appropriate number of feature changes needed within an image.

Conclusions

Ray tracing with POV-Ray is addictive. It can cause you to spend many hours at your computer that you could otherwise spend painting your house or mowing your lawn. After a short learning curve, however, you'll be producing unique and spectacular imagery.

References

Angell, Ian O. High Resolution Computer Graphics using C. New York, NY: John Wiley, 1990.

Glassner, Andrew S., ed. An Introduction to Ray Tracing. San Diego, CA: Academic Press, 1989.

Glassner, Andrew S. "Ray Tracing for Realism." BYTE (December 1990).

Joy, Kenneth I., Charles W. Grant, Nelson L. Max, and Lansing Hatfield, eds. Computer Graphics: Image Synthesis. Los Alamitos, CA: IEEE Computer Society Press, 1988.

Lindley, Craig A. Practical Ray Tracing in C. New York, NY: John Wiley, 1992.

------. Practical Image Processing in C. New York, NY: John Wiley, 1991.

Lyke, Daniel. "Ray Tracing." Dr. Dobb's Journal (September 1990).

van Dam, A. and Foley, J.D. Fundamentals of Interactive Computer Graphics. Reading, MA: Addison-Wesley, 1983.

Example 1: Pseudocode description of backward ray tracing.

Procedure Ray Trace
Begin
    For each row of the image  {
       For each column of the image  {
          Generate a ray from the eye's location through the pixel at (column,row)
          For each object in 3D universe  {
            Calculate the intersection between the ray and object
            If intersection occurred  {
               If intersection distance is the smallest yet (object is closest to eye)  {
                 Save this intersection
               }
            }
          }
          If an intersection did occur  {
             Calculate point of intersection of ray with closest object
             Calculate normal to object's surface at the point of intersection
             For each light source in 3D universe  {
                Generate a ray from the point of intersection to light source
                For each object in 3D universe except the one initially intersected  {
                   Calculate light ray/object intersection
                   If no intersection occurred, point of intersection is not in shadow  {
                     Calculate pixel's color value and intensity as a function of this light source
                   }
                }
             }
          }  else  {      // No intersections with objects occurred
             Set pixels color and intensity to that of the background
          }
         Color pixel on screen and/or save pixel data to a file
       }     // column
    }        // row
End

Figure 1 The image-development process.

Figure 2 A basic image generated by the scene-description file in Listing One.

Figure 3 A complex image generated by a POV-Ray scene-description file.

Listing One


// A basic scene with sphere and floor. See <a href="/showArticle.jhtml?documentID=ddj9407h&pgno=7">Figure 2</A>. 

#include "colors.inc"

// Define the camera location and orientation
camera  {

   location <0.0  5.0  -20.0>
   direction <0.0 0.0  1.0>
   up  <0.0  1.0  0.0>
   right <1.0 0.0 0.0>
}
// Define a simple point light source
object  {
   light_source  {
      <30 30 -30>
      color White
   }
}
// Floor plane 
object {
   plane { <0.0 1.0 0.0> 0.0 }
   texture {
      color Blue
   }
}
// Sphere object 
object  {
   sphere  {  <0 3 0> 3 }
   texture {
      color Red
      phong 5
   }
}


Listing Two


// Define a starry black sky
#declare Sky = object {
  plane { < 0 0 1> 5000 }
  color Black
  texture {
    bozo
    turbulence 0.3
    color_map {
      [0.0 0.9   color Black color Black ]
      [0.9 1.001 color Black color White ]
     }
    scale < 50 50 50 >
   }
 }
// Define the checkered floor. NOTE: the floor is partially reflective.
#declare Floor = object {
  intersection {
    plane { <-1  0  0 >  100 }
    plane { < 1  0  0 >  100 }
    plane { < 0 -1  0 >    1 }
    plane { < 0  1  0 >    0 }
    plane { < 0  0 -1 >  100 }
    plane { < 0  0  1 >    6 }
   }
  texture { checker
    color Blue
    color MidnightBlue
    scale < 12 12 12 >
    ambient 0.4

    diffuse 0.6
    reflection 0.4
  }
 }
// Define the infinite path. It is reflective and checkered also.
#declare PathWay = object {
  intersection {
    plane { <-1  0  0 >   18 }
    plane { < 1  0  0 >   18 }
    plane { < 0 -1  0 >    1 }
    plane { < 0  1  0 >    0 }
    plane { < 0  0 -1 >   -6 }
    plane { < 0  0  1 > 5000 }
   }
  texture { checker
    color Blue
    color MidnightBlue
    scale < 12 12 12 >
    ambient 0.3
    diffuse 0.7
    reflection 0.4
  }
 }
// Define the wall with window and door cutouts.
#declare Wall = object {
  difference {
    intersection { // The solid wall
      plane { <-1  0  0 >  100 }
      plane { < 1  0  0 >  100 }
      plane { < 0 -1  0 >    0 }
      plane { < 0  1  0 >   96 }
      plane { < 0  0 -1 >    0 }
      plane { < 0  0  1 >    6 }
     }
    intersection { // Door Cutout
      plane { <-1  0  0 >   22   }
      plane { < 1  0  0 >   22   }
      plane { < 0 -1  0 >    0   }
      plane { < 0  1  0 >   80   }
      plane { < 0  0 -1 >    0.1 }
      plane { < 0  0  1 >    7   }
     }
    intersection { // Left window cutout
      plane { <-1  0  0 >  100   }
      plane { < 1  0  0 >  -36   }
      plane { < 0 -1  0 >  -12   }
      plane { < 0  1  0 >   76   }
      plane { < 0  0 -1 >    0.1 }
      plane { < 0  0  1 >    5   }
     }
    intersection { // Right window cutout
      plane { <-1  0  0 >  -36   }

      plane { < 1  0  0 >  100   }
      plane { < 0 -1  0 >  -12   }
      plane { < 0  1  0 >   76   }
      plane { < 0  0 -1 >    0.1 }
      plane { < 0  0  1 >    5   }
     }
   }
  // Wall is bounded for speed
  bounded_by {
    intersection { // The complete wall
      plane { <-1  0  0 >  100 }
      plane { < 1  0  0 >  100 }
      plane { < 0 -1  0 >    0 }
      plane { < 0  1  0 >   96 }
      plane { < 0  0 -1 >    0 }
      plane { < 0  0  1 >    6 }
     }
   }
  // The wall has a near white color
  texture {
    ambient 0.4
    diffuse 0.6
    color VLightGrey
  }
 }
// Define the view which is the cloudy blue sky seen through the windows.
#declare View = composite {
  object {
    // View through the left window
    intersection {
      plane { <-1  0  0 >  100    }
      plane { < 1  0  0 >  -36    }
      plane { < 0 -1  0 >  -12    }
      plane { < 0  1  0 >   76    }
      plane { < 0  0 -1 >   -4.98 }
      plane { < 0  0  1 >    4.99 }
     }
    // This is the cloudy blue sky.
    texture {
      ambient 0.8
      diffuse 0.0
      turbulence 0.5
      bozo
      color_map {
        [0.0 0.6   color red 0.4  green 0.4  blue 1.0
                   color red 0.4  green 0.4  blue 1.0]
        [0.6 0.8   color red 0.4  green 0.4  blue 1.0
                   color red 1.0  green 1.0  blue 1.0]
        [0.8 1.001 color red 1.0  green 1.0  blue 1.0
                   color red 0.85 green 0.85 blue 0.85]
       }
      scale < 75 15 75 >

     }
   }
  // View through right window
  object {
    intersection {
      plane { <-1  0  0 >  -36    }
      plane { < 1  0  0 >  100    }
      plane { < 0 -1  0 >  -12    }
      plane { < 0  1  0 >   76    }
      plane { < 0  0 -1 >   -4.98 }
      plane { < 0  0  1 >    4.99 }
     }
    // This is the cloudy blue sky.
    texture {
      ambient 0.8
      diffuse 0.0
      turbulence 0.5
      bozo
      color_map {
        [0.0 0.6   color red 0.4  green 0.4  blue 1.0
                   color red 0.4  green 0.4  blue 1.0]
        [0.6 0.8   color red 0.4  green 0.4  blue 1.0
                   color red 1.0  green 1.0  blue 1.0]
        [0.8 1.001 color red 1.0  green 1.0  blue 1.0
                   color red 0.85 green 0.85 blue 0.85]
       }
      translate < 25 10 0 >
      scale < 50 15 75 >
     }
   }
 }


Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.