12.5 Area Lights

Area lights are light sources defined by one or more Shapes that emit light from their surface, with some directional distribution of radiance at each point on the surface. In general, computing radiometric quantities related to area lights requires computing integrals over the surface of the light that often can’t be computed in closed form. This issue is addressed with the Monte Carlo integration techniques in Section 14.2. The reward for this complexity (and computational expense) is soft shadows and more realistic lighting effects, rather than the hard shadows and stark lighting that come from point lights. (See Figure 12.16.)

Figure 12.16: Wider View of the Lighting Example Scene. The dragon is illuminated by a disk area light source directly above it.

Figure 12.17 shows the effect of varying the size of an area light source used to illuminate the dragon; compare its soft look to illumination from a point light in Figure 12.6.

Figure 12.17: Dragon Model Illuminated by Disk Area Lights. (1) The disk’s radius is relatively small; the shadow has soft penumbrae, but otherwise the image looks similar to the one with a point light. (2) The effect of using a much larger disk: not only have the penumbrae become much larger, to the point of nearly eliminating the fully in-shadow areas, but notice how areas like the neck of the dragon and its jaw have noticeably different appearances when illuminated from a wider range of directions.

The AreaLight class is an abstract base class that inherits from Light. Implementations of area lights should inherit from it.

<<Light Declarations>>+= 
class AreaLight : public Light { public: <<AreaLight Interface>> 
AreaLight(const Transform &LightToWorld, const MediumInterface &medium, int nSamples) : Light((int)LightFlags::Area, LightToWorld, medium, nSamples) { } virtual Spectrum L(const Interaction &intr, const Vector3f &w) const = 0;
};

AreaLight adds a single new method to the general Light interface, AreaLight::L(). Implementations are given a point on the surface of the light represented by an Interaction and should evaluate the area light’s emitted radiance,  upper L Subscript Superscript , in the given outgoing direction.

<<AreaLight Interface>>= 
virtual Spectrum L(const Interaction &intr, const Vector3f &w) const = 0;

For convenience, there is a method in the SurfaceInteraction class that makes it easy to compute the emitted radiance at a surface point intersected by a ray.

<<SurfaceInteraction Method Definitions>>+= 
Spectrum SurfaceInteraction::Le(const Vector3f &w) const { const AreaLight *area = primitive->GetAreaLight(); return area ? area->L(*this, w) : Spectrum(0.f); }

DiffuseAreaLight implements a basic area light source with a uniform spatial and directional radiance distribution. The surface it emits from is defined by a Shape. It only emits light on the side of the surface with outward-facing surface normal; there is no emission from the other side. (The Shape::reverseOrientation value can be set to true to cause the light to be emitted from the other side of the surface instead.) DiffuseAreaLight is defined in the files lights/diffuse.h and lights/diffuse.cpp.

<<DiffuseAreaLight Declarations>>= 
class DiffuseAreaLight : public AreaLight { public: <<DiffuseAreaLight Public Methods>> 
DiffuseAreaLight(const Transform &LightToWorld, const MediumInterface &mediumInterface, const Spectrum &Le, int nSamples, const std::shared_ptr<Shape> &shape); Spectrum L(const Interaction &intr, const Vector3f &w) const { return Dot(intr.n, w) > 0.f ? Lemit : Spectrum(0.f); } Spectrum Power() const; Spectrum Sample_Li(const Interaction &ref, const Point2f &u, Vector3f *wo, Float *pdf, VisibilityTester *vis) const; Float Pdf_Li(const Interaction &, const Vector3f &) const; Spectrum Sample_Le(const Point2f &u1, const Point2f &u2, Float time, Ray *ray, Normal3f *nLight, Float *pdfPos, Float *pdfDir) const; void Pdf_Le(const Ray &, const Normal3f &, Float *pdfPos, Float *pdfDir) const;
protected: <<DiffuseAreaLight Protected Data>> 
const Spectrum Lemit; std::shared_ptr<Shape> shape; const Float area;
};

<<DiffuseAreaLight Method Definitions>>= 
DiffuseAreaLight::DiffuseAreaLight(const Transform &LightToWorld, const MediumInterface &mediumInterface, const Spectrum &Lemit, int nSamples, const std::shared_ptr<Shape> &shape) : AreaLight(LightToWorld, mediumInterface, nSamples), Lemit(Lemit), shape(shape), area(shape->Area()) { }

<<DiffuseAreaLight Protected Data>>= 
const Spectrum Lemit; std::shared_ptr<Shape> shape; const Float area;

Because this area light implementation emits light from only one side of the shape’s surface, its L() method just makes sure that the outgoing direction lies in the same hemisphere as the normal.

<<DiffuseAreaLight Public Methods>>= 
Spectrum L(const Interaction &intr, const Vector3f &w) const { return Dot(intr.n, w) > 0.f ? Lemit : Spectrum(0.f); }

The DiffuseAreaLight::Sample_Li() method isn’t as straightforward as it has been for the other light sources described so far. Specifically, at each point in the scene, radiance from area lights can be incident from many directions, not just a single direction as was the case for the other lights (Figure 12.18). This leads to the question of which direction should be chosen for this method. We will defer answering this question and providing an implementation of this method until Section 14.2, after Monte Carlo integration has been introduced.

Figure 12.18: An area light casts incident illumination along many directions, rather than from a single direction.

Emitted power from an area light with uniform emitted radiance over the surface can be directly computed in closed form:

<<DiffuseAreaLight Method Definitions>>+=  
Spectrum DiffuseAreaLight::Power() const { return Lemit * area * Pi; }