②
One nice property of mesh-based shapes like triangle meshes and
subdivision surfaces is that the shape’s vertices can be transformed into
rendering space, so that it is not necessary to transform rays into object space
before performing ray intersection tests. Interestingly enough, it is
possible to do the same thing for ray–quadric intersections.
The implicit forms of the quadrics in this chapter were all of the form
where some of the constants were zero. More generally, we can
define quadric surfaces by the equation
where most of the parameters do not directly correspond to the
earlier
. In this form, the quadric can be represented by a
symmetric matrix :
Given this representation, first show that the matrix representing a
quadric transformed by the matrix is
To do so, show that for any point where , if we
apply a transformation to and compute , we would like to find
so that .
Next, substitute the ray equation into the earlier, more general quadric equation
to compute coefficients for the quadratic equation in
terms of entries of the matrix to pass to the Quadratic() function.
Now implement this approach in pbrt and use it instead of the original
quadric intersection routines. Note that you will still need to transform
the resulting rendering space hit points into object space to test against
, if it is not , and so on. How does performance compare to the
original scheme?
② Transforming the object-space bounding box of a quadric
to rendering space does not necessarily give an optimal bounding box.
However, the matrix form of a quadric described in Exercise 6.1 can also be
applied to computing optimal bounds. Read the article by Barnes
(2014) on this topic and implement the approach he
described in pbrt. How much are bounding boxes improved with this
approach? Measure the effect of your changes on rendering performance for
a scene with many transformed quadrics.
① Improve the object-space bounding box routines for the quadrics
to properly account for , and compute tighter bounding boxes
when possible. How much does this improve performance when rendering
scenes with partial quadric shapes?
② There is room to optimize the implementations of the
various quadric primitives in pbrt in a number of ways. For example, for
complete spheres some of the tests in the intersection routine related to
partial spheres are unnecessary. Furthermore, some of the quadrics have
calls to trigonometric functions that could be turned into simpler
expressions using insight about the geometry of the particular primitives.
Investigate ways to speed up these methods. How much does doing so improve
the overall run time of pbrt when rendering scenes with quadrics?
① Fix the buggy Sphere::Sample() and Disk::Sample()
methods, which currently do not properly account for partial spheres and
disks when they sample points on the surface. Create a scene that
demonstrates the error from the current implementations and for which your
solution is clearly an improvement.
② It is possible to derive a sampling method for cylinder
area light sources that only chooses points over the visible area as seen
from the receiving point, similar to the improved sphere sampling method in
this chapter (Gardner et al. 1987;
Zimmerman 1995). Write a new implementation of Cylinder::Sample()
that implements such an algorithm. Verify that pbrt still generates
correct images with your method, and measure how much the improved version
reduces variance for a fixed number of samples taken. How much does it
improve efficiency? How do you explain any discrepancy between the amount
of reduction in variance and the amount of improvement in efficiency?
② Implement one of the approaches for sampling the spheres
according to the projected solid angle in their visible region
(Ureña and Georgiev 2018; Peters and Dachsbacher 2019). Measure the change in pbrt’s
execution time when the alternative algorithm is used and discuss your
results.
Then, measure the MSE of pbrt’s current approach as well as your approach
for a few scenes with spherical light sources, using an image rendered with
thousands of samples per pixel as a reference. How do the results differ
if the light is always unoccluded versus if it is sometimes partially
occluded? How does the BSDF of scene surfaces affect the results?
① Currently pbrt recomputes the partial derivatives
and for triangles every time they are needed, even though they are
constant for each triangle. Precompute these vectors and analyze the
speed/storage trade-off, especially for large triangle meshes. How do
the depth complexity of the scene and the size of triangles in the image
affect this trade-off?
② Implement a general polygon primitive that supports an
arbitrary number of vertices and convex or concave polygons as a new
Shape in pbrt. You can assume that a valid polygon has been
provided and that all the vertices of the polygon lie on the same plane,
although you might want to issue a warning when this is not the case.
An efficient technique for computing ray–polygon intersections is to find
the plane equation for the polygon from its normal and a point on the
plane. Then compute the intersection of the ray with that plane and
project the intersection point and the polygon vertices to 2D. You can then apply
a 2D point-in-polygon test to determine if the point is inside the polygon.
An easy way to do this is to effectively do a 2D ray-tracing computation:
intersect the ray with each of the edge segments, and count how many it
goes through. If it goes through an odd number of them, the point is
inside the polygon and there is an intersection. See
Figure 6.47 for an illustration of this idea.
You may find it helpful to read the article by Haines (1994) that surveys a
number of approaches for efficient point-in-polygon tests. Some of the
techniques described there may be helpful for optimizing this test.
Furthermore, Section 13.3.3 of Schneider and Eberly (2003) discusses
strategies for getting all the corner cases right: for example, when the 2D
ray is aligned precisely with an edge or passes through a vertex of the
polygon.
Figure 6.47: A ray–polygon intersection test can be
performed by finding the point where the ray intersects the polygon’s
plane, projecting the hit point and polygon vertices onto an axis-aligned
plane, and doing a 2D point-in-polygon test there.
② Constructive solid geometry (CSG) is a solid
modeling technique where complex shapes are built up by considering the
union, intersection, and differences of more primitive shapes. For
example, a sphere could be used to create pits in a cylinder if a shape was
modeled as the difference of a cylinder and set of spheres that partially
overlapped it. See Hoffmann (1989) for further information about CSG.
Add support for CSG to pbrt and render images that demonstrate
interesting shapes that can be rendered using CSG. You may want to read
Roth (1982), which first described how ray tracing could be used to render
models described by CSG, as well as Amanatides and Mitchell (1990), which
discusses accuracy-related issues for CSG ray tracing.
②Procedurally described parametric surfaces: Write a
Shape that takes a general mathematical expression of the form
that describes a parametric surface as a
function of . Evaluate the given function at a grid of
positions, and create a bilinear patch mesh that approximates the given
surface. Render images of interesting shapes using your new Shape.
②Adaptive curve refinement: Adjust the number of levels of
recursive refinement used for intersection with Curve shapes based on
the on-screen area that they cover. One approach is to take advantage of
the RayDifferential class, which represents the image space area that
a given ray represents. (However, currently, only Rays—not
RayDifferentials—are passed to the Shape::Intersect()
method implementation, so you would need to modify other parts of the system to make ray
differentials available.) Alternatively, you could modify the Camera
to provide information about the projected length of vectors between points
in rendering space on the image plane and make the camera available during
Curve intersection.
Render images that show the benefit of adaptive refinement when the camera
is close to curves. Measure performance, varying the camera-to-curves
distance. Does performance improve when the camera is far away? How does
it change when the camera is close?
② Implement one of the more efficient ray–curve
intersection algorithms described by Reshetov (2017) or by
Reshetov and Luebke (2018). Measure the performance of
pbrt’s current Curve implementation as well as your new one and
discuss the results. Do rendered images match with both approaches? Can
you find differences in the intersections returned that lead to changes in
images, especially when the camera is close to a curve? Explain your
findings.
③Ray-tracing point-sampled geometry: Extending methods for
rendering complex models represented as a collection of point samples
(Levoy and Whitted 1985; Pfister et al. 2000;
Rusinkiewicz and Levoy 2000), Schaufler and Jensen (2000) have described a method for intersecting rays with
collections of oriented point samples in space. Their algorithm probabilistically
determined that an intersection has occurred when a ray approaches a
sufficient local density of point samples and computes a surface normal
with a weighted average of the nearby samples. Read their paper and extend
pbrt to support a point-sampled geometry shape. Do any of pbrt’s basic
interfaces need to be extended or generalized to support a shape like this?
③Deformation motion blur: The TransformedPrimitive in
Section 7.1.2 of Chapter 7 supports animated
shapes via transformations of primitives that vary over time. However,
this type of animation is not general enough to represent a triangle mesh
where each vertex has a position given at the start time and another one at
the end time. (For example, this type of animation description can be used
to describe a running character model where different parts of the body
are moving in different ways.)
Implement a more general Triangle or BilinearPatch shape that supports specifying
vertex positions at the start and end of frame and interpolates between
them based on the ray time passed to the intersection methods. Be sure to
update the bounding routines appropriately.
Meshes with very large amounts of motion may exhibit poor
performance due to individual triangles or patches sweeping out large bounding boxes and thus
many intersection tests being performed that do not hit the
shape. Can you come up with approaches that could be used to reduce the
impact of this problem?
③Implicit functions: Just as implicit definitions of the
quadric shapes are a useful starting point for deriving ray-intersection
algorithms, more complex implicit functions can also be used to define
interesting shapes. In particular, difficult-to-model organic shapes,
water drops, and so on can be well represented by implicit surfaces.
Blinn (1982a) introduced the idea of directly rendering implicit surfaces,
and Wyvill and Wyvill (1989) gave a basis function for implicit surfaces
with a number of advantages compared to Blinn’s.
Implement a method for finding ray intersections with implicit
surfaces and add it to pbrt. You may wish to read papers by Kalra and
Barr (1989), Hart (1996), and Sabbadin and
Droske (2021) for methods for ray
tracing them. Mitchell’s algorithm for robust ray intersections with
implicit surfaces using interval arithmetic gives another effective method
for finding these intersections (Mitchell 1990), and more recently
Knoll et al. (2009) described refinements to this idea. You
may find an approach along these lines easier to implement than the others.
See Moore’s book on interval arithmetic as needed for reference
(Moore 1966).
③L-systems: A very successful technique for procedurally
modeling plants was introduced to graphics by Alvy Ray Smith (1984), who
applied Lindenmayer systems (L-systems) to model branching plant
structures. Prusinkiewicz and collaborators have generalized this approach
to encompass a much wider variety of types of plants and effects that
determine their appearance (Prusinkiewicz 1986; Prusinkiewicz et al. 1994;
Deussen et al. 1998; Prusinkiewicz et al. 2001).
L-systems describe the
branching structure of these types of shapes via a grammar. The grammar
can be evaluated to form expressions that describe a topological
representation of a plant, which can then be translated into a geometric
representation. Add an L-system primitive to pbrt that takes a grammar
as input and evaluates it to create the shape it describes.
① Given an arbitrary point , what bound on the error
from applying a scale transformation of is given by
Equation (6.30)? How much error is actually
introduced?
② The quadric shapes all use the Interval class for
their intersection tests in order to be able to bound the error in the
computed value so that intersections behind the ray origin are not
incorrectly reported as intersections. First, measure the
performance difference when using regular Floats for one or more
quadrics when rendering a scene that includes those shapes. Next, manually
derive conservative error bounds for values computed by those shapes as
was done for triangles in Section 6.8.7.
Implement your method.
You may find it useful to use the Interval
class to empirically test your derivation’s correctness. Measure the
performance difference with your implementation.
② One detail thwarts the watertightness of the current
Triangle shape implementation: the translation and shearing of triangle vertices
introduces round-off error, which must be accounted for in the extent of
triangles’ bounding boxes; see Section 3.3 of Woop et al. (2013) for
discussion (and a solution). Modify pbrt to incorporate a solution to
this shortcoming. Can you find scenes where small image errors are
eliminated thanks to your fix?