6.3 Cylinders
Another useful quadric is the cylinder; pbrt provides a cylinder Shape that is centered around the axis. The user can supply a minimum and maximum value for the cylinder, as well as a radius and maximum sweep value (Figure 6.7).
In parametric form, a cylinder is described by the following equations:
Figure 6.8 shows a rendered image of two cylinders. Like the sphere image, the right cylinder is a complete cylinder, while the left one is a partial cylinder because it has a value less than .
Similar to the Sphere constructor, the Cylinder constructor takes transformations that define its object space and the parameters that define the cylinder itself. Its constructor just initializes the corresponding member variables, so we will not include it here.
6.3.1 Area and Bounding
A cylinder is a rolled-up rectangle. If you unroll the rectangle, its height is , and its width is :
As was done with the sphere, the cylinder’s spatial bounding method computes a conservative bounding box using the range but does not take into account the maximum .
Its surface normal bounding function is conservative in two ways: not only does it not account for , but the actual set of normals of a cylinder can be described by a circle on the sphere of all directions. However, DirectionCone’s representation is not able to bound such a distribution more tightly than with the entire sphere of directions, and so that is the bound that is returned.
6.3.2 Intersection Tests
Also similar to the sphere (and for similar reasons), Cylinder provides a BasicIntersect() method that returns a QuadricIntersection as well as an InteractionFromIntersection() method that converts that to a full SurfaceInteraction. Given these, the Intersect() method is again a simple composition of them. (If pbrt used virtual functions, a design alternative would be to have a QuadricShape class that provided a default Intersect() method and left BasicIntersect() and InteractionFromIntersection() as pure virtual functions for subclasses to implement.)
The form of the BasicIntersect() method also parallels the sphere’s, computing appropriate quadratic coefficients, solving the quadratic equation, and then handling the various cases for partial cylinders. A number of fragments can be reused from the Sphere’s implementation.
As before, the fragment that computes the quadratic discriminant, <<Compute cylinder quadratic discriminant discrim>>, is defined in Section 6.8.3 after topics related to floating-point accuracy have been discussed.
As with spheres, the ray–cylinder intersection formula can be found by substituting the ray equation into the cylinder’s implicit equation. The implicit equation for an infinitely long cylinder centered on the axis with radius is
When we expand this equation and find the coefficients of the quadratic equation , we have
As with spheres, the implementation refines the computed intersection point to reduce the rounding error in the point computed by evaluating the ray equation; see Section 6.8.5. Afterward, we invert the parametric description of the cylinder to compute from and ; it turns out that the result is the same as for the sphere.
The next step in the intersection method makes sure that the hit is in the specified range and that the angle is acceptable. If not, it rejects the hit and checks if it has not already been considered—these tests resemble the conditional logic in Sphere::Intersect().
For a successful intersection, the same three values suffice to provide enough information to later compute the corresponding SurfaceInteraction.
As with the sphere, IntersectP()’s implementation is a simple wrapper around BasicIntersect().
InteractionFromIntersection() computes all the quantities needed to initialize a SurfaceInteraction from a cylinder’s QuadricIntersection.
Again the parametric value is computed by scaling to lie between 0 and 1. Inversion of the parametric equation for the cylinder’s value gives the parametric coordinate.
The partial derivatives for a cylinder are easy to derive:
We again use the Weingarten equations to compute the parametric partial derivatives of the cylinder normal. The relevant partial derivatives are
6.3.3 Sampling
Uniformly sampling the surface area of a cylinder is straightforward: uniform sampling of the height and give uniform area sampling. Intuitively, it can be understood that this approach works because a cylinder is just a rolled-up rectangle.
Given and , the corresponding object-space position and normal are easily found.
Unlike the Sphere, pbrt’s Cylinder does not have a specialized solid angle sampling method. Instead, it samples a point on the cylinder uniformly by area without making use of the reference point before converting the area density for that point to a solid angle density before returning it. Both the Sample() and PDF() methods can be implemented using the same fragments that were used for solid angle sampling of reference points inside spheres.