## 14.3 Direct Lighting

Before we introduce the light transport equation in its full generality, we
will implement the `DirectLightingIntegrator` which, unsurprisingly,
only accounts for direct lighting—light that has traveled directly
from a light source to the point being shaded—and ignores indirect
illumination from objects that are not themselves emissive, except for
basic specular reflection and transmission effects. Starting out with this
integrator allows us to focus on some of the important details of direct
lighting without worrying about the full light transport equation.
Furthermore, some of the routines developed here will be used again in
subsequent integrators that solve the complete light transport equation.
Figure 14.12 shows the San Miguel scene rendered
with direct lighting only.

*(Model courtesy of Guillermo M. Leal Llaguno.)*

The implementation provides two different strategies for computing
direct lighting. Each computes an unbiased estimate of exitant
radiance at a point in a given direction. The `LightStrategy`
enumeration records which approach has been selected. The first strategy,
`UniformSampleAll`, loops over all of the lights and takes a number of
samples based on `Light::nSamples` from each of them, summing the
result. (Recall the discussion of splitting in
Section 13.7.1—the `UniformSampleAll` strategy is
applying this technique.) The second, `UniformSampleOne`, takes a
single sample from just one of the lights, chosen at random.

Depending on the scene being rendered, either of these approaches may be more appropriate. For example, if many image samples are being taken for each pixel (e.g., to resolve depth of field without excessive noise), then a single light sample per image sample may be more appropriate: in the aggregate all of the image samples in a pixel will sample the direct lighting well enough to give a high-quality image. Alternatively, if few samples per pixel are being taken, sampling all lights may be preferable to ensure a noise-free result.

The `DirectLightingIntegrator` constructor, which we won’t include
here, just passes a `Camera` and `Sampler` to the
`SamplerIntegrator` base class constructor and initializes two member
variables.
In addition to the direct lighting strategy, the
`DirectLightingIntegrator` stores a maximum recursion depth for
rays that are traced to account for specular reflection or specular transmission.

The numbers and types of samples needed by this integrator depend on the
sampling strategy used: if a single sample is taken from a single light,
then two two-dimensional samples obtained via `Sampler::Get2D()`
suffice—one for selecting a position on a light source and one for
sampling a scattered direction from the `BSDF`.

When multiple samples are taken per light, the integrator requests
sample arrays from the sampler before the main rendering process
begins. Using sample arrays is preferable to performing many separate calls
to `Sampler::Get2D()` because it gives the sampler an opportunity to
use improved sample placement techniques, optimizing the distribution of
samples over the entire array and thus, over all of the light source
samples for the current point being shaded.

For the `LightStrategy::UniformSampleAll` strategy, each light stores
a desired number of samples in its `Light::nSamples` member variable.
However, the integrator here only uses that value as a starting point: the
`Sampler::RoundCount()` method is given an opportunity to change that
value to a more appropriate one based on its particular sample generation
technique. (For example, many samplers only generate collections of
samples with power-of-two sizes.) The final number of samples for each
light is recorded in the `nLightSamples` member variable.

Now the sample arrays can be requested. There are two important details in
this fragment: first, although a separate sample request is made for all of
the possible ray depths up to the maximum, this doesn’t mean that all
intersections will have sample arrays available. Rather, once a sample
array is retrieved by a call to `Sampler::Get2DArray()`, that array
won’t be returned again. If both specular reflection and transmission are
present, then there may be up to intersection points
for each camera ray intersection. If the sample arrays are exhausted,
the integrator switches to taking a single sample from each light.

Second, note that the arrays are requested in the order they will be
consumed by the integrator:
at each intersection point, two arrays are used for each light source, in
the same order as the `lights` array.

As a `SamplerIntegrator`, the main method that the
`DirectLightingIntegrator` must implement is
`Li()`. The general form
of its implementation here is similar to that of
`WhittedIntegrator::Li()`: the BSDF at the intersection point is
computed, emitted radiance is added if the surface is emissive, rays are
traced recursively for specular reflection and transmission, and so on. We
won’t include the full implementation of
`DirectLightingIntegrator::Li()` here in order to focus on its key
fragment, <<Compute direct lighting for
`DirectLightingIntegrator` integrator>>, which estimates the value of
the integral that gives the reflected radiance, accumulating it into a
value `L` that will be returned from `Li()`.

Two helper functions, which other integrators will also find useful, take care of the two sampling strategies.

`DirectLightingIntegrator`integrator>>=

To understand the approaches implemented by the two strategies, first
recall the scattering equation from Section 5.6,
which says that exitant radiance from a point
on a surface in direction due to incident radiance at the point is
given by an integral of incoming radiance over the sphere times the BSDF
for each direction and a cosine term. For the
`DirectLightingIntegrator`, we are only interested in incident
radiance directly from light sources, which we will denote by
:

This can be broken into a sum over the lights in the scene

where denotes incident radiance from the th light and

One valid approach is to estimate each term of the sum in Equation (14.12) individually,
adding the results together. This is the most basic direct lighting
strategy and is implemented in `UniformSampleAllLights()`.

In addition to information about the intersection point and additional
parameters that are needed to compute the direct lighting,
`UniformSampleAllLights()` also takes a `handleMedia` parameter
that indicates whether the effects of volumetric attenuation should be
considered in the direct lighting computation. (Between this parameter and
the detail that it takes an `Interaction` rather than a
`SurfaceInteraction`, this function is actually able to compute
reflected radiance at points in participating media; fragments related to
this functionality will be defined in the next chapter.)

`j`th light to

`L`>>

`light`>>

This function attempts to retrieve the sample arrays from the `Sampler`
that were previously requested in the `Preprocess()` method.

`j`th light to

`L`>>=

`light`>>

If all of the requested arrays have been consumed, the code falls back to a
single sample estimate via calls to `Sampler::Get2D()`.

`light`>>=

For each light sample, the `EstimateDirect()` function (which will be
defined shortly) computes the value of the Monte Carlo estimator for its
contribution. When sample arrays were successfully obtained, all that
remains to be done is to average the estimates from each of their sample values.

In a scene with a large number of lights, it may not be desirable to always compute direct lighting from all of the lights at every point that is shaded. The Monte Carlo approach gives a way to do this that still computes the correct result on average. Consider as an example computing the expected value of the sum of two functions . If we randomly evaluate just one of or and multiply the result by 2, then the expected value of the result will still be . This idea also generalizes to sums with an arbitrary number of terms; see Ross (2002, p. 102) for a proof. Here we estimate direct lighting for only one randomly chosen light and multiply the result by the number of lights to compensate.

`light`>>

Which of the `nLights` to sample illumination from is determined using a
1D sample from `Sampler::Get1D()`.

`light`>>=

It’s possible to be even more creative in choosing the individual light
sampling probabilities than the uniform method used in
`UniformSampleOneLight()`. In fact, we’re free to set the
probabilities any way we like, as long as we weight the result
appropriately and there is a nonzero probability of sampling any light that
contributes to the reflection at the point. The better a job we do at
setting these probabilities to reflect the relative contributions of lights
to reflected radiance at the reference point, the more efficient the Monte
Carlo estimator will be, and the fewer rays will be needed to lower
variance to an acceptable level. (This is just the discrete instance of
importance sampling.)

One widely used approach to this task is to base the sample distribution on the total power of each light. In a similar manner, we could take more than one light sample with this approach; indeed, any number of samples can be taken in the end, as long as they are weighted appropriately.

### 14.3.1 Estimating the Direct Lighting Integral

Having chosen a particular light to estimate direct lighting from, we need to estimate the value of the integral

for that light. To compute this estimate, we need to choose one or more directions and apply the Monte Carlo estimator:

To reduce variance, we will use importance sampling to choose the directions . Because both the BSDF and the direct radiance terms are individually complex, it can be difficult to find sampling distributions that match their product well. (However, see the “Further Reading” section as well as Exercise 14.8 at the end of this chapter for approaches that sample their product directly.) Here we will use the BSDF’s sampling distribution for some of the samples and the light’s for the rest. Depending on the characteristics of each of them, one of these two sampling methods may be far more effective than the other. Therefore, we will use multiple importance sampling to reduce variance for the cases where one or the other is more effective.

Figure 14.13 shows cases where one of the sampling methods is much better than the other. In this scene, four rectangular surfaces ranging from very smooth (top) to very rough (bottom) are illuminated by spherical light sources of increasing size. Figures 14.13(1) and (2) show the BSDF and light sampling strategies on their own, while Figure 14.13(3) shows their combination computed using multiple importance sampling. As the example illustrates, sampling the BSDF can be much more effective when it takes on large values on a narrow set of directions that is much smaller than the set of directions that would be obtained by sampling the light sources. This case is most visible in the top left reflection of a large light source in a low-roughness surface. On the other hand, sampling the light sources can be considerably more effective in the opposite case—when the light source is small and the BSDF lobe is less concentrated (this case is most visible in the bottom right reflection).

By applying multiple importance sampling, not only can we use both of the two sampling methods, but we can also do so in a way that eliminates the extreme variance from the cases where a sampling method unexpectedly finds a high-contribution direction, since the weighting terms from MIS reduce these contributions substantially.

`EstimateDirect()` implements this approach and computes a direct
lighting estimate for a single light source sample. Its `handleMedia`
parameter indicates whether the effect of attenuation from participating
media should be accounted for, and its `specular` parameter indicates
whether or not perfectly specular lobes should be considered in the direct
illumination estimate. The default value for both `specular` and
`handleMedia` arguments is set to `false` in the function
declaration, which is not shown here.

`wi`>>

First, one sample is taken from the light’s sampling distribution using
`Sample_Li()`, which also returns the light’s emitted radiance and the
value of the PDF for the sampled direction.

Only if the light successfully samples a direction and returns nonzero
emitted radiance does `EstimateDirect()` go ahead and evaluate the
BSDF or phase function at the provided `Interaction`; otherwise,
there’s no reason to go through the computational expense. (Consider, for
example, a spotlight, which returns no radiance for points outside its
illumination cone.)

(We postpone the medium-specific fragment that evaluates the phase function at the interaction point, <<Evaluate phase function for light sampling strategy>>, to Chapter 15.)

If participating media are to be accounted for, the radiance from the light
to the illuminated point is scaled by the beam transmittance between the
two points to account for attenuation due to participating
media. Otherwise, a call to the `VisibilityTester`’s
`Unoccluded()` method traces a shadow ray to determine if the sampled
point on the light source is visible. (This step and the next are skipped
if the BSDF or phase function returned a black SPD.)

The light sample’s contribution can now be accumulated. Recall from
Section 14.2.1 that if the light is described
by a delta distribution then there is an implied delta distribution in
both the emitted radiance value returned from `Sample_Li()` as well as
the PDF and that they are expected to cancel out when the estimator is
evaluated. In this case, we must not try to apply multiple importance
sampling and should compute the standard estimator instead. If this isn’t
a delta distribution light source, then the
BSDF’s PDF value for sampling the direction , which was
returned by `BSDF::Pdf()`, is used with the MIS estimator,
where the weight is computed here with the power heuristic.

Next, a sample is generated using the BSDF’s sampling distribution. This step should be skipped if the light source’s emission profile involves a delta distribution because, in that case, there’s no chance that sampling the BSDF will give a direction that receives light from the source. Otherwise, the BSDF can be sampled.

`wi`>>

Once more, we postpone the medium-related code to Chapter 15. Given a surface interaction, the implementation samples a scattered direction and records whether or not a delta distribution was sampled.

One important detail is that the light’s PDF and the multiple importance sampling weight are only computed if the BSDF component used for sampling is non-specular; in the specular case, MIS shouldn’t be applied since there is no chance of the light sampling the specular direction.

`wi`>>=

Given a direction sampled by the BSDF or a medium’s phase function, we need to find out if the ray along that direction intersects this particular light source and if so, how much radiance from the light reaches the surface. When participating media are being accounted for, the transmittance up to the intersection point on the light is recorded.

The code must account for both regular area lights, with geometry associated
with them, as well as lights like the `InfiniteAreaLight` that don’t have
geometry but need to return their radiance for the sample ray via the
`Light::Le()` method.