September 20, 2022

Improved Blue Noise


Our GPU implementation of Void And Cluster blue noise worked well for us. It provides many tweaking parameters that help to remove low noise frequencies even more, even if the blue noise texture is small. The extension for spatiotemporal effects was simple: fract(blue_noise + per_frame_random).

Previous attempts to use multiple blue noise texture sequences didn’t work well. The simple fract(bs+rnd) wrap provides the same white noise signal over time as multiple uncorrelated blue noise textures. Nvidia Spatiotemporal Blue Noise extension for blue noise removes white noise over time. It improves the image quality without additional cost because each frame renders in absolutely the same way except for different random seeds for effects.

We wanted to keep our blue noise benefits like excellent performance and variable sigma with maximum code reuse of the current algorithm. And the idea was simple: use the Void And Cluster algorithm for layers and add correlation between layers. So we added a few lines to the current implementation that uses the last blue noise result as an input seed for the new generation. That preserves the quality of blue noise per layer, but what about time correlation? The result was promising. The amplitude of low frequencies over time started to be lower but still not perfect. That is because the next iteration of blue noise generation makes the inverse sequence, and the result was almost the same as a layer before. After a few tries of different input seed jittering and randomization, we found that a simple image flip over the X and Y axes provides the best quality for time coherence.

Here is the sequence of 64 blue noise (64×64) images spectrum over XY/XZ/YZ slices with different sigma (2.0, 1.6, 1.2). As you can see, there are no low frequencies in time at all, and we can still control the number of middle frequencies in the XY slice as before (8×8 grid of layers):

Responsive image
Responsive image
Responsive image
Responsive image
Responsive image
Responsive image
Responsive image
Responsive image
Responsive image

Now let’s see the difference between a single wrapped blue noise image (white over time) and a sequence of blue noise images (blue over time). 64x64x32 noise with a 1.6 sigma value works best for us. This is a side-by-side comparison of the PCF shadow map with just 8 samples (super fast on modern GPUs). Almost all unwanted blue noise tiling artifacts are gone:

Responsive image
Responsive image

A comparison with Nvidia STBN (128x128x64) also shows image improvement on ray tracing ambient occlusion with 2spp and reflection with 1spp. And it’s interesting because we still use 64x64x32 noise that is 8 times smaller in size. “Single” is the first layer from the noise sequence with white noise wrap. “All” is all layers from the sequence.

The high-level source code of the improved blue noise generator is available on our GitHub page. The source code requires Tellusim Engine, but you can use it as a starting point in your noise explorations.

Ready-to-use binaries for Windows, Linux, and macOS:

Ready-to-use noise sequences with 1.6 sigma:

  • 64x64x32: png ktx
  • 64x64x64: png ktx
  • 128x128x64: png ktx (108 seconds on M1 Max).
  • 256x256x128: png ktx (21 minutes on M1 Max).

We are thankful for the following Sketchfab models that were used in this article: