Environment Map Emitter (15 pts)
Implementation
Added files:
environment_emitter.cpp
environment_emitter_naive.cpp
An environment map emitter surrounds the whole scene and is assumed to be infinitely far away.
My environment emitter implements the Emitter
interface and loads an EXR image through the Bitmap
class.
Without importance sampling, the implementation is quite straightforward and can be found in the environment_emitter_naive.cpp
file,
which was only kept due to validation purposes.
In this class, we essentially use Warp::squareToUniformSphere
to sample a direction on the sphere, and then spherically map this direction
to uv coordinates through the dirToUv
function.
Environment lights have to be treated differently than other emitters in the integrators. In case a ray does not intersect any geometry,
it will always hit our environment light. For this purpose, I have added a getEnvironmentEmitter
function to the scene
class.
Environment emitter support was added to multiple integrators, notably path_mis, path_mats and volpath_mats.
The tricky part of this feature lies in importance sampling the environment light. We importance sample based on the luminance of each pixel.
I have followed the approach described in this paper
for implementing this functionality.
In summary, we use that $p(u, v) = p(u | v) * p(v)$, which allows us to sample from a 2D distribution by sampling twice from a 1D distribution, namely
by first sampling a row through the marginal density, then sampling a column inside that row through the conditional density for the sampled row.
We then need to convert our sampled uv
vector to a 3D direction, which is the job of the uvToDir
function (relies on the
already implemented sphericalDirection
function).
An important aspect that further improves our result is to multiply the luminance with $sin(\theta)$ to account for the equirectangular projection.
Validation
I validated my implementation by comparing with Mitsuba. Here are two scenes consisting of three spheres with a mirror, diffuse and dielectric BSDF (from left to right). These spheres are solely lit by an environment map emitter. Both presented environment maps were obtained from PolyHaven.
Room Environment (256 spp)


Overcast Outdoor Environment (256 spp)


Effectiveness of Importance Sampling (256 spp)
I furthermore assessed the effectiveness of my importance sampling by comparing it to simple uniform spherical sampling. Here is a comparison of a implementation using uniform spherical sampling to the implementation using importance sampling, both with 256 samples per pixel. As expected, the variant using importance sampling contains considerably less noise.


2D Importance Sampling
Next to the visibly reduced noise, I further tested whether I am correctly sampling according to the luminance of the environment map.
As I lacked the required safety goggles for adapting the warptest.cpp
file, I made my own simplified version of it inside the precompute
function of environment_emitter.cpp
.
Essentially, I am generating two images, one with the luminance (multiplied with the sine term) of the environment map corresponding to our 2D PDF, and one
where I am generating a lot of samples and every time I sample a specific pixel, I increase the brightness of this pixel, which essentially
generates a 2D histogram.
In the following comparison one can see that my sample placement closely matches the luminance of the environment map:

