Environment Map Emitter

Modified Files:

include/warp.h

src/envmap.cpp

src/warp.cpp

src/bitmap.cpp

src/path_mis_env.cpp

src/path_mis_all.cpp

Small Changes: include/emitter.h, include/scene.h, src/parser.cpp, src/scene.cpp

Implementation

The implementation follows the Infinite Area Lights of PBRT but without Mipmap technique.
A EnvironmentMapEmitter which is a subclass of Emitter is created for the purpose. The accepted environment map format is EXR.

Emitter Object

In the constructor of the EnvironmentMapEmitter, the provided image in EXR format is read into the Bitmap provided by Nori. And then, a piecewise-constant 2D probability distribution is created with class Distr2D for importance sampling.
The environment emitter is an emitter in the infinite distance, therefore in my implementation, the emitter is not bounded by any Shape.

Integrator Behavior

In the path integrator, when a ray doesn't intersect with any object, it intersects with the environment emitters. Therefore, before the path tracing stop, the environment emitters are evaluated and the emitter color is added to the final result with the MIS weights.
While doing the emitter sampling, the environment emitters are sampled and evaluated just as normal emitters.

Emitter Eval

The environment emitter is in the infinite distance, therefore the reference position doesn't matter, only the direction of the ray will influence the evaluation. For a given ray direction, it's first transformed to the emitter coordinates and then converted to spherical coordinates. In the end, it's converted to the uv coordinates of the stored bitmap. And the result is the bilinear interpolation of the bitmap.

Emitter Sampling

A naive sampling method is simply uniformly sampling a sphere direction. This sampling will lead to a very high variance.
To reduce the variance, importance sampling based on the luminance of the pixels is required. To do so, a piecewise-constant 2D probability distribution is created in the constructor. And to build a more robust distribution to sample from, the pixels used to build the distribution are blurred by computing an interpolation of nearby pixels.
Sampling from 1D discrete distribution can simply follow the inverted CDF method, and sampling 2D discrete distribution can also do a similar thing to firstly sample from a 1D marginal distribution and then a 1D conditioned distribution. To do so, the marginal and conditioned distribution need to be computed in advance upon the construction of the distribution object.

Validation

Piecewise-constant 2D distribution

To validate the distribution, some simple unit tests are done in the src/tesclass.cpp. For example:

A test example

A test example

Environment Emitter

(1) In the first validation, I show a teapot with a dielectric body and diffuse lid in the background of an environment map emitter. The result is compared against Mitsuba. I also compare the result of uniform sampling and importance sampling of the emitter. It's obvious that the variance is much smaller with importance sampling, especially on the diffuse lid. All the result is rendered with 256spp.

The scene file are in scenes/envmap/teapot.

Mine_Uniform Mine_Importance Mitsuba

A teapot with environment map emitter.


(2) In this validation, I used the table scene and used another environment map to show that the result is still consistent. All the result is rendered with 256spp.

The scene file are in scenes/envmap/table.

Mine_Importance Mitsuba

Table scene with environment map emitter.