## 12.3 Point Lights

A number of interesting lights can be described in terms of emission from a
single point in space with some possibly angularly varying distribution of
outgoing light. This section describes the implementation of a number of
them, starting with `PointLight`, which represents an isotropic
point light source that emits the same amount of light in all directions.
It is defined in `lights/point.h` and
`lights/point.cpp`.
Figure 12.6 shows a scene rendered with a point light
source. Building on this base,
a number of more complex lights based on
point sources will be introduced, including spotlights and a light that
projects an image into the scene.

`PointLight`s are positioned at the origin in light space. To place
them elsewhere, the light-to-world transformation should be set
appropriately. Using this transformation, the world space position of the
light is precomputed and cached in the constructor by transforming
from light space to world space.

The constructor also stores the intensity for the light source, which is
the amount of power per unit solid angle. Because the light source is
isotropic, this is a constant. Finally, since point lights represent
singularities that only emit light from a single position, the
`Light::flags` field is initialized to `LightFlags::DeltaPosition`.

Strictly speaking, it is incorrect to describe the light arriving at a
point due to a point light source using units of radiance. Radiant
intensity is instead the proper unit for describing emission from a point
light source, as explained in Section 5.4. In the light
source interfaces here, however, we will abuse terminology
and use `Sample_Li()` methods to report the illumination arriving at a
point for all types of light sources, dividing radiant intensity by the
squared distance to the point p to convert units. Section 14.2
revisits the details of this issue in its discussion of how delta
distributions affect evaluation of the integral in the scattering equation.
In the end, the correctness of the computation does not suffer from this
fudge, and it makes the implementation of light transport algorithms
more straightforward by not requiring them to use different interfaces for
different types of lights.

The total power emitted by the light source can be found by integrating the intensity over the entire sphere of directions:

### 12.3.1 Spotlights

Spotlights are a handy variation on point lights; rather than shining
illumination in all directions, they emit light in a cone of directions
from their position. For simplicity, we will define the spotlight in the
light coordinate system to always be at position and pointing
down the axis. To place or orient it elsewhere in the scene, the
`Light::WorldToLight` transformation should be set accordingly.
Figure 12.7 shows a rendering of the same scene as
Figure 12.6, only illuminated with a spotlight
instead of a point light. The `SpotLight` class is defined in
`lights/spot.h` and
`lights/spot.cpp`.

Two angles are passed to the constructor to set the extent of the
`SpotLight`’s cone: the overall angular width of the cone and the
angle at which falloff starts (Figure 12.8). The
constructor precomputes
and stores the cosines of these angles for use in the `SpotLight`’s
methods.

*falloffStart*and

*totalWidth*. Objects inside the inner cone of angles, up to

*falloffStart*, are fully illuminated by the light. The directions between

*falloffStart*and

*totalWidth*are a transition zone that ramps down from full illumination to no illumination, such that points outside the

*totalWidth*cone aren’t illuminated at all. The cosine of the angle between the vector to a point p and the spotlight axis, , can easily be computed with a dot product.

The `SpotLight::Sample_Li()` method is almost identical to
`PointLight::Sample_Li()`, except that it also calls the
`Falloff()` method, which computes the distribution of light
accounting for the spotlight cone. This computation is encapsulated in a
separate method since other `SpotLight` methods will need to perform
it as well.

To compute the spotlight’s strength for a receiving point , this first step is to compute the cosine of the angle between the vector from the spotlight origin to and the vector along the center of the spotlight’s cone. To compute the cosine of the offset angle to a point p, we have, as illustrated in Figure 12.8,

This value is then compared to the cosines of the falloff and overall width angles to see where the point lies with respect to the spotlight cone. We can trivially determine that points with a cosine greater than the cosine of the falloff angle are inside the cone receiving full illumination, and points with cosine less than the width angle’s cosine are completely outside the cone. (Note that the computation is slightly tricky since for , if , then .)

For points inside the transition range from fully illuminated to outside of the cone, the intensity is scaled to smoothly fall off from full illumination to darkness:

The solid angle subtended by a cone with spread angle is
. Therefore, the integral over directions on the
sphere that gives power from radiant intensity can be solved to compute the total power of a light that only emits
illumination in a cone. For the spotlight, we can reasonably
approximate the power of the light by computing the solid angle of
directions that is covered by the cone with a spread angle cosine halfway between
`falloffWidth` and `falloffStart`.

### 12.3.2 Texture Projection Lights

Another useful light source acts like a slide projector; it takes an image
map and projects its image out into the scene. The `ProjectionLight`
class uses a projective transformation to project points in the scene onto
the light’s projection plane based on the field of view angle given to the
constructor (Figure 12.9). Its implementation is in
`lights/projection.h` and
`lights/projection.cpp`.

The use of this light in the lighting example scene is shown in Figure 12.10.

This light could use a `Texture` to represent the light projection
distribution so that procedural projection patterns could be used.
However, having a precise representation of the projection function, as is
available by using an image in a `MIPMap`, is useful for being able to
sample the projection distribution using Monte Carlo techniques, so we will
use that representation in the implementation here.

Note that the projected image is explicitly stored as an `RGBSpectrum`,
even if full spectral rendering is being performed. Unless the image map
being used has full spectral data, storing full `SampledSpectrum`
values in this case only wastes memory; whether an RGB color is converted
to a `SampledSpectrum` before the `MIPMap` is created or after the
`MIPMap` returns a value from its `Lookup()` routine gives the
same result in either case.

Similar to the `PerspectiveCamera`, the `ProjectionLight`
constructor computes a projection matrix and the screen space extent of the
projection.

Finally, the constructor finds the cosine of the angle between the axis and the vector to a corner of the screen window. This value is used elsewhere to define the minimal cone of directions that encompasses the set of directions in which light is projected. This cone is useful for algorithms like photon mapping that need to randomly sample rays leaving the light source (explained in Chapter 16). We won’t derive this computation here; it is based on straightforward trigonometry.

Similar to the spotlight’s version,
`ProjectionLight::Sample_Li()`
calls a utility
method, `ProjectionLight::Projection()`, to determine how much light is
projected in the given direction. Therefore, we won’t include the
implementation of `Sample_Li()` here.

Because the projective transformation has the property that it projects points behind the center of projection to points in front of it, it is important to discard points with a negative value. Therefore, the projection code immediately returns no illumination for projection points that are behind the near plane for the projection. If this check were not done, then it wouldn’t be possible to know if a projected point was originally behind the light (and therefore not illuminated) or in front of it.

After being projected to the projection plane, points with coordinate
values outside the screen window are discarded. Points that pass this test
are transformed to get texture coordinates inside for the
lookup in the image map. Note it is explicitly specified that the
`RGBSpectrum` value passed to the `Spectrum` constructor represents
an illuminant’s SPD and not that of a reflectance. (Recall from
Section 5.2.2 that different matching functions are used for
converting from RGB to SPDs for illuminants versus reflectances.)

The total power of this light is approximated as a spotlight that subtends the same angle as the diagonal of the projected image, scaled by the average intensity in the image map. This approximation becomes increasingly inaccurate as the projected image’s aspect ratio becomes less square, for example, and it doesn’t account for the fact that texels toward the edges of the image map subtend a larger solid angle than texels in the middle when projected with a perspective projection. Nevertheless, it’s a reasonable first-order approximation.

### 12.3.3 Goniophotometric Diagram Lights

A *goniophotometric diagram* describes the angular distribution of
luminance from a point light source; it is widely used in illumination
engineering to characterize lights.
Figure 12.11 shows an example of a
goniophotometric diagram in two
dimensions. In this section, we’ll implement a light source that uses
goniophotometric diagrams encoded in 2D image maps to describe the emission
distribution of the light. The implementation is very similar to the point
light sources defined previously in this section; it scales the intensity
based on the outgoing direction according to the goniophotometric diagram’s
values.

Figure 12.12 shows a few goniophotometric diagrams encoded as image maps.

Figure 12.13 shows a scene rendered with a light source that uses one of these images to modulate its directional distribution of illumination.

`mipmap`for

`GonioPhotometricLight`>>

The `GonioPhotometricLight` constructor takes a base intensity and an
image map that scales the intensity based on the angular distribution of
light.

`mipmap`for

`GonioPhotometricLight`>> }

Like `ProjectionLight`, `GonioPhotometricLight` constructs a
`MIPMap` of the distribution’s image map, also always using
`RGBSpectrum` values.

`mipmap`for

`GonioPhotometricLight`>>=

The `GonioPhotometricLight::Sample_Li()` method is not shown here.
It is essentially identical to the `SpotLight::Sample_Li()` and
`ProjectionLight::Sample_Li()` methods that use a helper function to
scale the amount of radiance. It assumes that the scale texture is
encoded using spherical coordinates, so that the given direction needs
to be converted to and values and scaled to before
being used to index into the
texture. Goniophotometric diagrams are usually defined in a coordinate
space where the axis is up, whereas the spherical coordinate utility
routines in `pbrt` assume that is up, so and are swapped before
doing the conversion.

The `Power()` method uses the average intensity over the image to
compute power. This computation is inaccurate because the spherical
coordinate parameterization of directions has various distortions,
particularly near the and directions. Again, this error is
acceptable for the uses of this method in `pbrt`.