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
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
.
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
.
Table scene with environment map emitter.