Alan Wolfe’s paper, “Using Blue Noise for Ray Traced Soft Shadows”, presents a great method for reducing the perceived noise in soft shadows. It can be readily adapted to a path tracer as well, where it will yield images with better clarity, even with only a few samples.
Take a look at an image rendered with regular RNG white noise in the left half, and blue noise in the right half:
Using blue noise results in much better clarity even though a lot of noise is still present in this 1spp image!
Adapting the method for a path tracer requires a small modification in order to avoid strange-looking spatial correlations in the noise pattern.
Path tracer adaptations
The method presented in the paper uses a golden ratio low-discrepancy sequence in place of a random number for each frame. Neighboring pixels are spatially decorrelated by adding blue noise to each golden ratio sequence value, to prevent pixels from having an identical noise value each frame.
|
|
2D sequences and noise
Unlike the paper, 2D noise is used here in order to randomly sample a hemisphere. Various golden ratio sequence can be obtained from “The Unreasonable Effectiveness of Quasirandom Sequences”. The pseudocode for the n th term of a 2D golden ratio sequence is defined as
g = 1.32471795724474602596
a1 = 1.0/g
a2 = 1.0/(g*g)
x[n] = (0.5+a1*n) %1
y[n] = (0.5+a2*n) %1
The very regular pattern of this sequence (called the “R2 sequence” in the plot below) can be seen by plotting N values and comparing to white noise.
Using the 2D golden ratio sequences values in place of random numbers when sampling a cosine-weighted hemisphere results in a very regular distribution about the hemisphere.
Pre-calculated blue noise values can be downloaded from Moments in Graphics. These blue noise mask textures are tileable, and I found that a 128x128 mask was enough to render images without tiling artifacts.
Putting these together by naively following the paper (i.e. plugging 2D blue noise and the R2 sequence into an equivalent AnimatedBlueNoise
function) results in images with a strange unmotivated shadow.
Animating blue noise per bounce
On each ray bounce, a new direction needs to be computed. If the animated blue noise is computed per-frame, per-pixel, we end up reusing the same animated blue noise on each bounce, and thus keep generating the same hemisphere direction for each vertex along the path.
One way of solving this issue is to increment the frame index counter each time animated_blue_noise
function is called.
|
|
In this scheme, each bounce consumes a new value in the golden ratio sequence. To prevent consecutive frames from using overlapping golden ratio sequence values, we can initialize the frame index as a multiple of the number of bounces:
|
|
With these changes in place, the spatially correlated black pixels vanish from the basic 1spp render of cubes: