## 7.1 Primitive Interface and Geometric Primitives

The `Primitive` class defines the `Primitive` interface. It
and the `Primitive` implementations that are described in this
section are defined in the files `cpu/primitive.h` and
`cpu/primitive.cpp`.

The `Primitive` interface is composed of only three methods, each of which
corresponds to a `Shape` method. The first, `Bounds()`, returns a
bounding box that encloses the primitive’s geometry in rendering space. There
are many uses for such a bound; one of the most important is to place the
`Primitive` in the acceleration data structures.

The other two methods provide the two types of ray intersection tests.

Upon finding an intersection, a `Primitive`’s `Intersect()`
method is also responsible for initializing a few member variables in the
`SurfaceInteraction` in the `ShapeIntersection` that it returns. The
first two are representations of the shape’s material and its
emissive properties, if it is itself an emitter. For
convenience, `SurfaceInteraction` provides a method to set these, which
reduces the risk of inadvertently not setting all of them. The second two
are related
to medium scattering properties and the fragment that initializes them will
be described later, in Section 11.4.

### 7.1.1 Geometric Primitives

The `GeometricPrimitive` class provides a basic implementation of the
`Primitive` interface that stores a variety of properties that may be associated
with a shape.

Each `GeometricPrimitive` holds a `Shape` with a description
of its appearance properties, including its material, its emissive
properties if it is a light source, the participating media on each side of
its surface, and an optional *alpha texture*, which can be used to
make some parts of a shape’s surface disappear.

The `GeometricPrimitive` constructor initializes these variables from
the parameters passed to it. It is straightforward, so we do not include it
here.

Most of the methods of the `Primitive` interface start out with a call
to the corresponding `Shape` method. For example, its `Bounds()`
method directly returns the bounds from the `Shape`.

`GeometricPrimitive::Intersect()` calls the `Intersect()` method
of its `Shape` to do the actual intersection test and to initialize a
`ShapeIntersection` to describe the intersection, if any. If an
intersection is found, then additional processing specific to the
`GeometricPrimitive` is performed.

`SurfaceInteraction`after

`Shape`intersection>>

If an alpha texture is associated with the shape, then the intersection point is tested against the alpha texture before a successful intersection is reported. (The definition of the texture interface and a number of implementations are in Chapter 10.) The alpha texture can be thought of as a scalar function over the shape’s surface that indicates whether the surface is actually present at each point. An alpha value of 0 indicates that it is not, and 1 that it is. Alpha textures are useful for representing objects like leaves: a leaf might be modeled as a single triangle or bilinear patch, with an alpha texture cutting out the edges so that a detailed outline of a leaf remains.

If the alpha texture has a value of 0 or 1 at the intersection point, then it is easy to decide whether or not the intersection reported by the shape is valid. For intermediate alpha values, the correct answer is less clear.

One possibility would be to use a fixed threshold—for example, accepting
all intersections with an alpha of 1 and ignoring them otherwise. However,
this approach leads to hard transitions at the resulting boundary. Another
option would be to return the alpha from the intersection method and leave
calling code to handle it, effectively treating the surface as partially
transparent at such points. However, that approach would not only make the
`Primitive` intersection interfaces more complex, but it would place a
new burden on integrators, requiring them to compute the shading at
such intersection points as well as to trace an additional ray to find what
was visible behind them.

A *stochastic alpha test* addresses these issues. With it,
intersections with the shape are randomly reported with probability
proportional to the value of the alpha texture. This approach is easy to
implement, gives the expected results for an alpha of 0 or 1, and with a
sufficient number of samples gives a better result than using a fixed
threshold. Figure 7.1 compares the
approaches.

One challenge in performing the stochastic alpha test is generating a
uniform random number to apply it. For a given ray and shape, we would
like this number to be the same across multiple runs of the system; doing
so is a part of making the set of computations performed by `pbrt` be
deterministic, which is a great help for debugging. If a different random
number was used on different runs of the system, then we might hit a
runtime error on some runs but not others. However, it is important that
different random numbers be used for different rays; otherwise, the
approach could devolve into the same as using a fixed threshold.

The `HashFloat()` utility function provides a solution to this problem.
Here it is used to compute a random floating-point value between 0 and 1
for the alpha test; this value is determined by the ray’s origin and direction.

If the alpha test indicates that the intersection should be ignored, then
another intersection test is performed with the current
`GeometricPrimitive`, with a recursive call to `Intersect()`.
This additional test is important for shapes like spheres, where we may
reject the closest intersection but then intersect the shape again further
along the ray. This recursive call requires adjustment of the `tMax`
value passed to it to account for the distance along the ray to the initial
alpha tested intersection point. Then, if it reports an intersection, the
reported `tHit` value should account for that segment as well.

Given a valid intersection, the `GeometricPrimitive` can go ahead and
finalize the `SurfaceInteraction`’s representation of the intersection.

`SurfaceInteraction`after

`Shape`intersection>>=

The `IntersectP()` method must also handle the case of the
`GeometricPrimitive` having an alpha texture associated with it. In
that case, it may be necessary to consider all the intersections of the
ray with the shape in order to determine if there is a valid intersection.
Because `IntersectP()` implementations in shapes return early when
they find any intersection and because they do not return the geometric
information associated with an intersection, a full intersection test is
performed in this case. In the more common case of no alpha texture,
`Shape::IntersectP()` can be called directly.

Most objects in a scene are neither emissive nor have alpha textures.
Further, only a few of them typically represent the boundary between two
different types of participating media. It is wasteful to store
`nullptr` values for the corresponding member variables of
`GeometricPrimitive` in that common case. Therefore, `pbrt` also
provides `SimplePrimitive`, which also implements the `Primitive`
interface but does not store those values. The code that converts the
parsed scene representation into the scene for rendering uses a
`SimplePrimitive` in place of a `GeometricPrimitive` when it is
possible to do so.

Because `SimplePrimitive` only stores a shape and a material, it saves
32 bytes of memory. For scenes with millions of primitives, the overall
savings can be meaningful.

We will not include the remainder of the `SimplePrimitive`
implementation here; it is effectively a simplified subset of
`GeometricPrimitive`’s.

### 7.1.2 Object Instancing and Primitives in Motion

*(Scene courtesy of Laubwerk.)*

Object instancing is a classic technique in rendering that reuses
transformed copies of a single collection of geometry at multiple positions
in a scene. For example, in a model of a concert hall with thousands of
identical seats, the scene description can be compressed substantially if
all the seats refer to a shared geometric representation of a single
seat. The ecosystem scene in Figure 7.2 has 23,241
individual plants of various types, although only 31 unique plant models.
Because each plant model is instanced multiple times with a different
transformation for each instance, the complete scene has a total of 3.1
billion triangles. However, only 24 million triangles are stored in memory
thanks to primitive reuse through object instancing. `pbrt` uses just over
4 GB of memory when rendering this scene with object instancing (1.7 GB for
BVHs, 707 MB for `Primitive`s, 877 MB for triangle meshes, and 846 MB
for texture images), but would need upward of 516 GB to render it without
instancing.

The `TransformedPrimitive` implementation of the `Primitive`
interface makes object instancing possible in `pbrt`. Rather than holding a
shape, it stores a single `Primitive` as well as a `Transform`
that is injected in between the underlying primitive and its representation
in the scene. This extra transformation enables object instancing.

Recall that the `Shape`s of Chapter 6 themselves had
rendering from object space transformations applied to them to place them in
the scene. If a shape is held by a `TransformedPrimitive`, then the
shape’s notion of rendering space is not the actual scene rendering
space—only after the `TransformedPrimitive`’s transformation is also
applied is the shape actually in rendering space. For this application
here, it makes sense for the shape to not be at all aware of the additional
transformation being applied. For instanced primitives, letting
`Shape`s know all the instance transforms is of limited utility: we
would not want the `TriangleMesh` to make a copy of its vertex
positions for each instance transformation and transform them all the way
to rendering space, since this would negate the memory savings of object
instancing.

The `TransformedPrimitive` constructor takes a `Primitive`
that represents the model and the transformation that places it in the
scene. If the instanced geometry is described by multiple
`Primitive`s, the calling code is responsible for placing them in an
aggregate so that only a single `Primitive` needs to be stored
here.

The key task of `TransformedPrimitive` is to bridge between the
`Primitive` interface that it implements and the `Primitive`
that it holds, accounting for the effects of the rendering from primitive
space transformation. If the `primitive` member has its own
transformation, that should be interpreted as the transformation from
object space to the `TransformedPrimitive`’s coordinate system. The
complete transformation to rendering space requires both of these
transformations together.

The `Intersect()` method also must account for the
transformation, both for the ray passed to the held primitive and
for any intersection information it returns.

The method first transforms the
given ray to the primitive’s coordinate system and passes the transformed
ray to its `Intersect()` routine.

Given an intersection, the `SurfaceInteraction` needs to be transformed
to rendering space; the `primitive`’s intersection method will already
have transformed the `SurfaceInteraction` to its notion of rendering
space, so here we only need to apply the effect of the additional
transformation held by `TransformedPrimitive`.

Note that any returned `ShapeIntersection::tHit` value from the
primitive can be returned to the caller as is; recall the discussion of
intersection coordinate spaces and ray values in
Section 6.1.4.

The `IntersectP()`
method is similar and is therefore elided.

The `AnimatedPrimitive` class uses an `AnimatedTransform` in place
of the `Transform` stored by `TransformedPrimitive`s. It thus
enables rigid-body animation of primitives in the scene. See
Figure fig:spinning-spheres for an image that exhibits motion blur
due to animated transformations.

The `AnimatedTransform` class uses substantially more memory than
`Transform`. On the system used to develop `pbrt`, the former uses 696
bytes of memory, while the latter uses 128. Thus, just as was the case
with `GeometricPrimitive` and `SimplePrimitive`, it is worthwhile
to only use `AnimatedPrimitive` for shapes that actually are animated.
Making this distinction is the task of the code that constructs the scene
specification used for rendering.

A bounding box of the primitive over the frame’s time range is found via
the `AnimatedTransform::MotionBounds()` method.

We will also skip past the rest of the implementations of the
`AnimatedPrimitive` intersection methods; they parallel those of
`TransformedPrimitive`, just using an `AnimatedTransform`.