Graphics Reference
In-Depth Information
23
24
25
26
27
28
29
30
31
32
shade(scene, T, P, n, w_o, radiance);
// Debugging intersect: set to white on any intersection
//radiance = Radiance3(1, 1, 1);
// Debugging barycentric
//radiance = Radiance3(weight[0], weight[1], weight[2]) / 15;
return true
;
}
The
sampleRayTriangle
routine returns
false
if there was no intersec-
tion closer than
distance
; otherwise, it updates
distance
and
radiance
and
returns
true
.
When invoking this routine, the caller passes the
distance
to the clos-
est currently known intersection, which is initially
INFINITY
(let
INFINITY
= std::numeric_limits<T>::infinity()
in C++, or simply
1.0/0.0
).
We will design the
intersect
routine to return
INFINITY
when no inter-
section exists between
R
and
T
so that a missed intersection will never cause
sampleRayTriangle
to return
true
.
Placing the
(d >= distance)
test before the shading code is an optimiza-
tion. We would still obtain correct results if we always computed the shading
before testing whether the new intersection is in fact the closest. This is an impor-
tant optimization because the
shade
routine may be arbitrarily expensive. In fact,
in a full-featured ray tracer, almost all computation time is spent inside
shade
,
which recursively samples additional rays. We won't discuss further shading opti-
mizations in this chapter, but you should be aware of the importance of an early
termination when another surface is known to be closer.
Note that the order of the triangles in the calling routine (
rayTrace
) affects
the performance of the routine. If the triangles are in back-to-front order, then we
will shade each one, only to reject all but the closest. This is the worst case. If
the triangles are in front-to-back order, then we will shade the first and reject the
rest without further shading effort. We could ensure the best performance always
by separating
sampleRayTriangle
into two auxiliary routines: one to find the
closest intersection and one to shade that intersection. This is a common practice
in ray tracers. Keep this in mind, but do not make the change yet. Once we have
written the rasterizer renderer, we will consider the space and time implications of
such optimizations under both ray casting and rasterization, which gives insights
into many variations on each algorithm.
We'll implement and test
intersect
first. To do so, comment out the call to
shade
on line 23 and uncomment either of the debugging lines below it.
We'll find the intersection of the eye ray and a triangle in two steps, following the
method described in Section 7.9 and implemented in Listing 15.16. This method
first intersects the line containing the ray with the plane containing the triangle. It
then solves for the barycentric weights to determine if the intersection is within the
triangle. We need to ignore intersections with the back of the single-sided triangle
and intersections that occur along the part of the line that is not on the ray.
The same weights that we use to determine if the intersection is within the
triangle are later useful for interpolating per-vertex properties, such as shading