## 4.1 Primitive Interface and Geometric Primitives

The abstract `Primitive` base class is the bridge between the
geometry processing and shading subsystems of `pbrt`.

There are a number of geometric routines in the `Primitive` interface,
all of which are similar to a corresponding `Shape` method. The first,
`Primitive::WorldBound()`, returns a box that encloses the primitive’s
geometry in world 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 next two methods provide ray intersection tests.
One difference between the two base classes is that
`Shape::Intersect()` returns the parametric distance along the ray to
the intersection in a `Float *` output variable, while
`Primitive::Intersect()` is responsible for updating `Ray::tMax`
with this value if an intersection is found.

Upon finding an intersection, the `Primitive`’s `Intersect()` method is
also responsible for initializing additional `SurfaceInteraction`
member variables, including a
pointer to the `Primitive` that the ray hit.

`Primitive` objects have a few methods related to non-geometric
properties as well. The first, `Primitive::GetAreaLight()`,
returns a pointer to the `AreaLight` that describes the primitive’s
emission distribution, if the primitive is itself a light source. If the
primitive is not emissive, this method should return `nullptr`.

`GetMaterial()` returns a pointer to the material instance assigned to
the primitive. If `nullptr` is returned, ray intersections with the
primitive should be ignored; the primitive only serves to delineate a
volume of space for participating media. This method is also used to check if two
rays have intersected the same object by comparing their `Material`
pointers.

The third material-related method, `ComputeScatteringFunctions()`,
initializes representations of the light-scattering properties of the
material at the intersection point on the surface. The `BSDF` object
(introduced in Section 9.1) describes local light-scattering
properties at the intersection point. If applicable, this method also
initializes a `BSSRDF`, which describes subsurface scattering inside
the primitive—light that enters the surface at points far from where it
exits. While subsurface light transport has little effect on the
appearance of objects like metal, cloth, or plastic, it is the dominant
light-scattering mechanism for biological materials like skin, thick
liquids like milk, etc. The `BSSRDF` is supported by an extension of
the path tracing algorithm discussed in
Section 15.

In addition to a `MemoryArena` to allocate memory for the `BSDF`
and/or `BSSRDF`, this method takes a `TransportMode` enumerant that
indicates whether the ray path that found this intersection point started
from the camera or from a light source; as will be discussed further in
Section 16.1, this detail has important implications
for how some parts of material models are evaluated. The
`allowMultipleLobes` parameter controls a detail of how some types of
BRDFs are represented; it is discussed further in
Section 9.2.
Section 9.1.1 discusses the use of the
`MemoryArena` for `BSDF` memory allocation in more detail.

The `BSDF` and `BSSRDF` pointers for the point are stored in the
`SurfaceInteraction` passed to `ComputeScatteringFunctions()`.

### 4.1.1 Geometric Primitives

The `GeometricPrimitive` class represents a single shape (e.g., a sphere) in
the scene. One `GeometricPrimitive` is allocated for each shape in the
scene description provided by the user. This class is implemented in the files
`core/primitive.h` and
`core/primitive.cpp`.

Each `GeometricPrimitive` holds a reference to a `Shape` and its
`Material`. In addition, because primitives in `pbrt` may be
area light sources, it stores a pointer to an `AreaLight` object
that describes its emission characteristics (this pointer is set
to `nullptr` if the primitive does not emit light). Finally, the
`MediumInterface` attribute encodes information about the participating media on
the inside and outside of the primitive.

The `GeometricPrimitive` constructor just initializes these variables
from the parameters passed to it. It’s straightforward, so we don’t include
it here.

Most of the methods of the `Primitive` interface related to geometric
processing are simply forwarded to the corresponding `Shape` method.
For example, `GeometricPrimitive::Intersect()` calls the
`Shape::Intersect()` method of its enclosed `Shape` to do the
actual intersection test and initialize a `SurfaceInteraction`
to describe the intersection, if any. It also uses the returned parametric
hit distance to update the `Ray::tMax` member. The advantage
of storing the distance to the closest hit in `Ray::tMax` is that this
makes it easy to avoid performing intersection tests with any primitives
that lie farther along the ray than any already-found intersections.

`SurfaceInteraction::mediumInterface`after

`Shape`intersection>>

We won’t include the
implementations of the `GeometricPrimitive`’s `WorldBound()` or
`IntersectP()` methods here; they just forward these requests on to
the `Shape` in a similar manner. Similarly,
`GetAreaLight()` just
returns the `GeometricPrimitive::areaLight` member.

Finally, the `ComputeScatteringFunctions()` method just forwards the
request on to the `Material`.

### 4.1.2 TransformedPrimitive: Object Instancing and Animated Primitives

`TransformedPrimitive` holds a single `Primitive` and also includes an
`AnimatedTransform` that is injected in between the
underlying primitive and its representation in the scene. This extra
transformation enables two useful features: object instancing and
primitives with animated transformations.

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 of the seats refer to a shared geometric representation of a single
seat. The ecosystem scene in Figure 4.1 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, although only 24 million triangles are stored in memory,
thanks to primitive reuse through object instancing. `pbrt` uses just over
7 GB of memory when rendering this scene with object instancing (1.7 GB for
BVHs, 2.3 GB for triangle meshes, and 3 GB for texture maps), but would
need upward of 516 GB to render it without instancing.

Animated transformations enable rigid-body animation of primitives in the
scene via the `AnimatedTransform` class. See
Figure 2.15 for an image that exhibits motion blur
due to animated transformations.

Recall that the `Shape`s of Chapter 3 themselves had object-to-world
transformations applied to them to place them in the scene. If a shape is
held by a `TransformedPrimitive`, then the shape’s notion of world
space isn’t the actual scene world space—only after the
`TransformedPrimitive`’s transformation is also applied is the shape
actually in world space. For the applications here, it makes sense for the
shape to not be at all aware of the additional transformations being
applied. For animated shapes, it’s simpler to isolate all of the handling
of animated transformations to a single class here, rather than require all
`Shape`s to support `AnimatedTransform`s. Similarly, for instanced
primitives, letting `Shape`s know all of the instance transforms is of
limited utility: we wouldn’t want the `TriangleMesh` to make a copy of
its vertex positions for each instance transformation and
transform them all the way to world space, since this would negate the
memory savings of object instancing.

The `TransformedPrimitive` constructor takes a reference to the
`Primitive` that represents the model, and the transformation that
places it in the scene. If the geometry is described by multiple
`Primitive`s, the calling code is responsible for placing them in an
`Aggregate` implementation so that only a single `Primitive` needs
to be stored here. For the code that creates aggregates as needed, see the
`pbrtObjectInstance()` function in
Section A.3.6 of Appendix B for the case of
primitive instances, and see the `pbrtShape()` function in
Section A.3.5 for animated shapes.

The key task of the `TransformedPrimitive` is to bridge the
`Primitive` interface that it implements and the `Primitive` that
it holds a pointer to, accounting for the effects of the additional
transformation that it holds. The `TransformedPrimitive`’s
`PrimitiveToWorld` transformation defines the transformation from the
coordinate system of this particular instance of the geometry to world
space. 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 world space requires both of these transformations
together.

As such, the `TransformedPrimitive::Intersect()` method transforms the
given ray to the primitive’s coordinate system and passes the
transformed ray to its `Intersect()` routine.
If a hit is found, the `tMax` value from the transformed ray needs to be
copied into the ray `r` originally passed to the `Intersect()`
routine.

`ray`after transformation by

`PrimitiveToWorld`>>

To transform the ray, we need to interpolate the transformation based on
the ray’s time. Although we want to transform the ray `r` from world
space to primitive space, here we actually interpolate
`PrimitiveToWorld` and then invert the resulting `Transform` to
get the transformation. This surprising approach is necessary because of
how the polar decomposition-based transformation interpolation algorithm in
Section 2.9.3 works: interpolating
`PrimitiveToWorld` to some time and inverting it doesn’t necessarily
give the same result as interpolating its inverse, the animated world to
primitive transformation, directly. Because `Primitive::WorldBound()`
uses `PrimitiveToWorld` to compute the primitive’s bounding box, we
must also interpolate `PrimitiveToWorld` here for consistency.

`ray`after transformation by

`PrimitiveToWorld`>>=

Finally, the `SurfaceInteraction` at the intersection point needs to be
transformed to world space; the `primitive`’s intersection member will already have
transformed the `SurfaceInteraction` to its notion of world space, so
here we only need to apply the effect of the additional transformation held
here.

The rest of the geometric `Primitive` methods are forwarded on to the
shared instance, with the results similarly transformed as needed by
the `TransformedPrimitive`’s transformation.

The `TransformedPrimitive` `GetAreaLight()`,
`GetMaterial()`, and `ComputeScattering Functions()` methods
should never be called. The corresponding methods of the primitive that the
ray actually hit should always be called instead. Therefore, any attempt to
call the `TransformedPrimitive` implementations of these methods (not shown
here) results in a run-time error.