Rendering Techniques Assignment 3:

Ray Tracing Cylinders

Assigned: Tuesday, March 18, 2003
Due: Monday, March 31, 2003

Overview

For this assignment, you will add cylinder primitives to your ray tracer. Using an object-oriented approach, most of your program will apply to primitives without distinguishing between spheres and cylinders. Only a small portion of the program will deal with the particular primitives being rendered.

Program Description

Your extended raytracer should run with the same command line as the one from the previous assignment:
raytrace <scene description filename> <image output filename>
or
java raytrace <scene description filename> <image output filename>

The input format has been extended to describe a set of (right, circular) cylinders as well as the lights and spheres from the former assignments. Each cylinder is axis-aligned, and may be aligned with the X, Y, or Z axis. The cylinder may have infinite or finite length. Setting the length parameter to any negative value indicates an infinite length cylinder. Finite length cylinders begin with the base at the origin and extend for the specified length along the specified axis in the positive direction. Cylinders may be translated away from the actual axes by specifying the base center. For finite-length cylinders, this effectively specifies the location of the center of its base, and the rest of the cylinder extends in the positive direction of the specified axis. For inifinite-length cylinders, there is no actual base, but the base-center still describes the displacement of the cylinder from the coordinate axis.

Finite-length cylinders may also be rendered with or without caps (the caps will never be visible for infinite cylinders). These caps are planar discs that cover the ends of the cylinder, turning the finite cylinder into an enclosed solid.

The cylinders should be illuminated just the same as the spheres in the previous assignment. Thus, reflections and refractions are possible, as well as local illumination. As in the previous assignment, you should assume that transparent primitives do not overlap in space. Also, you may assume that finite cylinders without caps will not be transparent (because they are not really solids).

This assignment includes a sample scene (my test executable from the previous assignments works for this assignment as well). Be careful, there are a number of tricky situations that may occur in both the ray-cylinder intersection and normal computations. As always, perform test cases to verify the behavior of your program on data in all the geometric configurations you can think of.

Design Suggestions

For my implementation, I made a primitive class containing all the material properties and virtual functions for computing a ray intersection and for computing a normal vector given a surface point. Then I inherited all this to sphere and cylinder classes, each with their own intersection and normal functions.

Except for the file parser, intersection routines, and normal routines, all of my code knows only that it is dealing with a primitive or list of primitives. This makes the design much simpler and avoids coding things multiple times for the two primitive types.

Cylinder Mathematics

A cylinder is just a 2D circle extruded into three dimensions. This means that most of your computations may be performed effectively in 2D rather than 3D. In my implementation, I wrote a function to convert a point or vector from 3D to 2D, transferring the appropriate coordinates (depending on the cylinder axis) to the X and Y components of the 2D version, setting the Z component to 0.0. Thus, performing the intersection of a ray with the wall of a cylinder will involve converting the cylinder to a circle on the xy-plane, converting the ray into a ray on the xy-plane, and doing the intersection.

Notice that a circle is just a sphere in one lower dimension. This means that all the forumas you used for spheres pretty much apply to the cylinder computations. The implicit function for the sphere or circle is:

(X-C).(X-C) - r^2 = 0
Here, X is a point on the sphere or circle, C is the center, and r is the radius. The equation for the ray is:
X = P + t*V
P is the start of the ray and V is the direction. Substituting this formua for X in the implicit equation yields:
(P+tV-C).(P+tV-C) - r^2 = 0
Regrouping the terms by powers of t yields:
(V.V)*t^2 + 2*(P.V - C.V)*t + P.P + C.C - 2*P.C - r^2 = 0
Now you just solve for t using the quadratic formula to get 0, 1, or 2 possible intersections.

For cylinders with caps, you will also have to consider the intersections of the ray with the planes of the cylinder caps. Planes have the implicit form:

(N.X) - d = 0
N is the normal of the plane. For axis-aligned cylinders, the normal will be a unit vector in the direction of the axis and d is the distance of the plane from the origin. Substituting the ray for X yields:
N.(P+tV) - d = 0
N.P + t*(N.V) = d
t = (d - N.P) / (N.V)
There will be either 0 or 1 intersection. The intersection only counts if it is within the cylinder's circle, of course.

For capped cylinders, you will need to consider the intersection of a ray with not only the cylinder walls, but with the two caps. As always, the closest positive intersection is the one used. Also, for the normal computation, you will have to decide if the point is on a wall or cap to figure out what normal to return.

Deliverables

Submit a single .zip file  containing the following:

Scene Description Format (extension .rt3)

Numbers in angle brackets are real numbers, in square brackets are integers
Naturally, the brackets do not appear in the actual file (but the colons do).
Blank lines and white space should be ignored
Lines with a ‘#’ as the first character are comments and should be ignored.
Colors should be specified in the range [0.0, 1.0]
Angles should be in degrees, and refer to the full angle (as opposed to the half angle)
 
# comment lines start with # and may appear anywhere
RESOLUTION: [x resolution] [y resolution]
FIELD_OF_VIEW: <full x field-of-view>
BACKGROUND: <red> <green> <blue>
MAX_BOUNCES: [maximum recursion level]
NUM_LIGHTS: [number of lights]
LIGHT
 POSITION: <x> <y> <z>
 DIRECTION: <x> <y> <z>
 INTENSITY: <red> <green> <blue>
 EXPONENT: <exponent>
 CUTOFF: <angle>
ENDLIGHT
LIGHT
ENDLIGHT
LIGHT
ENDLIGHT
#in other words, one LIGHT…ENDLIGHT block for each light
NUM_SPHERES: [number of spheres]
#similarly, one SPHERE…ENDSPHERE block for each sphere
SPHERE
 CENTER: <x> <y> <z>
 RADIUS: <radius>
 DIFFUSE_COLOR: <red> <green> <blue>
 DIFFUSE_COEFFICIENT: <coefficient>
 SPECULAR_COLOR: <red> <green> <blue>
 SPECULAR_COEFFICIENT: <coefficient>
 SPECULAR_EXPONENT: <exponent>
 TRANSMISSION_COLOR: <red> <green> <blue>
 TRANSMISSION_COEFFICIENT: <coefficient>
 REFRACTION_INDEX: <index of refraction>
ENDSPHERE
SPHERE
ENDSPHERE
SPHERE
ENDSPHERE
# and finally, the cylinders
NUM_CYLINDERS: [number of cylinders]
CYLINDER
 AXIS: <X, Y, or Z>
 CAPS: <Y or N>
 BASE_CENTER: <x> <y> <z>
 RADIUS: <radius>
 LENGTH: <length>
 DIFFUSE_COLOR: <red> <green> <blue>
 DIFFUSE_COEFFICIENT: <coefficient>
 SPECULAR_COLOR: <red> <green> <blue>
 SPECULAR_COEFFICIENT: <coefficient>
 SPECULAR_EXPONENT: <exponent>
 TRANSMISSION_COLOR: <red> <green> <blue>
 TRANSMISSION_COEFFICIENT: <coefficient>
 REFRACTION_INDEX: <index of refraction>
ENDCYLINDER

Final Words of Encouragement

If you use some simple classes as I suggest, all notion of spheres and cylinders will be removed from most of your program, then you can focus on getting your cylinder intersection and normal computations correct. I recommend starting with infinite cylinders, then adding support for finite cylinders, and finally for rendering caps. Good luck, and have fun!



Home
March 18, 2003