12.4 Distant Lights

Another useful light source type is the distant light, also known as a directional light. It describes an emitter that deposits illumination from the same direction at every point in space. Such a light is also called a point light “at infinity,” since, as a point light becomes progressively farther away, it acts more and more like a directional light. For example, the sun (as considered from Earth) can be thought of as a directional light source. Although it is actually an area light source, the illumination effectively arrives at Earth in parallel beams because it is so far away. The DistantLight, implemented in the files lights/distant.h and lights/distant.cpp, implements a directional source.

<<DistantLight Declarations>>= 
class DistantLight : public Light { public: <<DistantLight Public Methods>> 
DistantLight(const Transform &LightToWorld, const Spectrum &L, const Vector3f &w); void Preprocess(const Scene &scene) { scene.WorldBound().BoundingSphere(&worldCenter, &worldRadius); } Spectrum Sample_Li(const Interaction &ref, const Point2f &u, Vector3f *wi, Float *pdf, VisibilityTester *vis) const; Spectrum Power() 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;
private: <<DistantLight Private Data>> 
const Spectrum L; const Vector3f wLight; Point3f worldCenter; Float worldRadius;
};

Note that the DistantLight constructor does not take a MediumInterface parameter; the only reasonable medium for a distant light to be in is a vacuum—if it was itself in a medium that absorbed any light at all, then all of its emission would be absorbed, since it’s modeled as being infinitely far away.

<<DistantLight Method Definitions>>= 
DistantLight::DistantLight(const Transform &LightToWorld, const Spectrum &L, const Vector3f &wLight) : Light((int)LightFlags::DeltaDirection, LightToWorld, MediumInterface()), L(L), wLight(Normalize(LightToWorld(wLight))) { }

<<DistantLight Private Data>>= 
const Spectrum L; const Vector3f wLight;

Some of the DistantLight methods need to know the bounds of the scene. Because lights are created before the scene geometry, these bounds aren’t available when the DistantLight constructor runs. Therefore, DistantLight implements the optional Preprocess() method to get the bound. (This method is called at the end of the Scene constructor.)

<<DistantLight Public Methods>>= 
void Preprocess(const Scene &scene) { scene.WorldBound().BoundingSphere(&worldCenter, &worldRadius); }

<<DistantLight Private Data>>+= 
Point3f worldCenter; Float worldRadius;

Most of the implementation of the Sample_Li() method is straightforward: for a distant light, the incident direction and radiance are always the same. The only interesting bit is the initialization of the VisibilityTester: here, the second point for the shadow ray is set along the distant light’s incident direction a distance of twice the radius of the scene’s bounding sphere, guaranteeing a second point that is outside of the scene’s bounds (Figure 12.14).

Figure 12.14: Computing the Second Point for a DistantLight Shadow Ray. Given a sphere that bounds the scene (dashed line) with radius r and given some point in the scene normal p Subscript , then if we move a distance of 2 r along any vector from normal p Subscript , the resulting point must be outside of the scene’s bound. If a shadow ray to such a point is unoccluded, then we can be certain that the point normal p Subscript receives illumination from a distant light along the vector’s direction.

<<DistantLight Method Definitions>>+=  
Spectrum DistantLight::Sample_Li(const Interaction &ref, const Point2f &u, Vector3f *wi, Float *pdf, VisibilityTester *vis) const { *wi = wLight; *pdf = 1; Point3f pOutside = ref.p + wLight * (2 * worldRadius); *vis = VisibilityTester(ref, Interaction(pOutside, ref.time, mediumInterface)); return L; }

The distant light is unusual in that the amount of power it emits is related to the spatial extent of the scene. In fact, it is proportional to the area of the scene receiving light. To see why this is so, consider a disk of area upper A being illuminated by a distant light with emitted radiance upper L Subscript Superscript where the incident light arrives along the disk’s normal direction. The total power reaching the disk is normal upper Phi equals upper A upper L Subscript Superscript . As the size of the receiving surface varies, power varies proportionally.

To find the emitted power for a DistantLight, it’s impractical to compute the total surface area of the objects that are visible to the light. Instead, we will approximate this area with a disk inside the scene’s bounding sphere oriented in the light’s direction (Figure 12.15). This will always overestimate the actual area but is sufficient for the needs of code elsewhere in the system.

Figure 12.15: An approximation of the power emitted by a distant light into a given scene can be obtained by finding the sphere that bounds the scene, computing the area of an inscribed disk, and computing the power that arrives on the surface of that disk.

<<DistantLight Method Definitions>>+=  
Spectrum DistantLight::Power() const { return L * Pi * worldRadius * worldRadius; }