A Low-Discrepancy Sampler that Distributes Monte Carlo Errors
as a Blue Noise in Screen Space



Eric Heitz
Laurent Belcour


Unity Technologies
Victor Ostromoukhov
David Coeurjolly
Jean-Claude Iehl


Universite Lyon I

LittlestTokyo - 1 spp

LittlestTokyo - 2 spp

LittlestTokyo - 3 spp

LittlestTokyo - 4 spp

Random scrambling

3D model by Glen Fox

A screen-space sampler ?
🤨

LittlestTokyo - 4 spp

Random scrambling

Correlated scrambling

⚠ not practical

Correlated scrambling

⚠ not practical

Random scrambling

Random scrambling

Inset

LittlestTokyo - 4 spp

Random scrambling

Correlated scrambling

⚠ not practical

Power Spectrum

Our goal

  • Distributes error as a blue-noise in screen-space
    • Reduces the visual error
    • Reduces the error after denoising
  • State-of-the-art sampler
  • Lean & efficient
    • Take two textures as input
    • Additional cost : two fetches & XORs
Inset
Power Spectrum

LittlestTokyo - 4 spp + Denoising

Random scrambling

Correlated scrambling

⚠ not practical

Blue-Noise Dithered Sampling

Georgiev and Fajardo [2016]

Blue-Noise Dithered Sampling

  • Builds on research in Dithering
    • Distribute quantization error
    • Blue-noise is perceptually better
  • Extending dither masks
    • nD dither mask
    • using Simulated Annealing
  • Scramble sequences
    • using Cranley-Patterson Rotations
    • Offset sample $k$ with dither at pixel $i,j$ $$ \mu_{k,i,j} = \mbox{mod}\left(\epsilon_k + d_{i,j}\right) $$

Georgiev and Fajardo [2016]


Density

$$ d(i,j) $$

Random Dithering

$$ d(i,j) > \mbox{rand}(i,j) $$

Blue-Noise Dithering

$$ d(i,j) > bn(i,j) $$

$bn(i,j)$


1D Dither Mask


3D Dither Mask

Blue-Noise Dithered Sampling

Problem solved ?

  1. How to generate different sequences?
  2. How to distribute them in screen-space?

Issue #1 : CPR Considered Harmful

CPR : shifting samples
equivalent : shifting integrand
break stratification

Issue #1 : CPR Considered Harmful

Integrand
Numerical Integrand Error

Solution #1 : XOR Scrambling




pmj02 sequence
Preserve multi-stratification
first dim. XOR key: 1 0 1 1

XOR scrambling [Kollig & Keller 2002]

CPR [Georgiev and Fajardo 2016]

Chair - 8 spp

Issue #2 : Lack of Progressivity

Boxed - 1 spp

Georgiev and Fajardo [2016]

Power Spectrum of Error

Issue #2 : Lack of Progressivity

Boxed - 16 spp

Georgiev and Fajardo [2016]

Power Spectrum of Error

Issue #2 : Lack of Progressivity

Boxed - 16 spp

Georgiev and Fajardo [2016]

Power Spectrum of Error
White-noise as SPP increase

Solution #2 : Change the Optimization Space

Our Method : Test Integrands

  • Optimize with constant image-space integrands
    • Same integrand for every pixel
    • But different XOR keys per pixel

Scrambling mask

Integrand
& Samples

Resulting Image

Our Method : Test Integrands

  • Optimize with constant image-space integrands
    • Same integrand for every pixel
    • But a different XOR key per pixel
  • Optimize using oriented Heavisides
    • Random orientation $\theta$ and offset $d$
    • Warning: $D$ dimensional integrands

Our Method : Test Integrands

  • Optimize with constant image-space integrands
    • Same integrand for every pixel
    • But a different XOR key per pixel
  • Optimize using oriented Heavisides
    • Random orientation $\theta$ and offset $d$
    • Warning: $D$ dimensional integrands
  • Robust to simple changes of integrand
    • Varying orientation
    • Varying smoothness
    • Varying topology
Integrand Integral - 1spp Power Spectrum

Our Method : Sorting

  • Optimize every power of two spp
    • Possible with (0,2)-sequences
    • Sorting using XOR of sample index

Our Method : Sorting

  • Optimize every power of two spp
    • Possible with (0,2)-sequences
    • Sorting using XOR of sample index
  • Permits progressivity
    • Blue-noise distribution across spp
Integrand 1spp 4spp 8spp 64spp 128spp

Summary


Scrambling mask


Sorting mask
function screen_space_sampler(i, j, index, dimension)
{
	// Fetch keys associated with pixel (i,j)
	scramble = scrambling_keys(i,j)
	sort = sorting_keys(i,j)

	// XOR the index
	index  = index ^ sort
	sample = sobol_owen(index, dimension)

	// XOR the sample
	sample = sample ^ scramble

	return sample
}
                            
2 texture fetches + 2 XORs

QMC Integration
What can we do with two XORs ?

Results: Offline Rendering

  • Direct Illumination in Mitsuba
    • Implemented a new Sampler
    • No impact on performances
  • Extensive study
    • See our supplemental material
    • Interactive HTML

Ours

Georgiev and Fajardo [2016]

Boxed - 1 spp

Ours

Georgiev and Fajardo [2016]

chair - 1 spp

Results: Real-Time Application

  • In the Unity Engine
    • Implementation by Thomas Deliot
    • Dithering Ambient Occlusion
    • Dithering Screen-Space Reflections
  • Performances on desktop
    • Nvidia 2080 GPU at 720p

    • White-noise Ours
      AO 0.26 ms 0.54 ms
      SSR 2.81 ms 3.06 ms

    • Fixed cost : 0.25 ms
  • Live demo !

Limitations : Another Hit in the Wall?

  • Share limitations with BNDS !
    • Can't handle high-dimensional integrands
    • Not robust on complex integrands
  • Still you should use our method rather than BNDS
    • Can only do better, not worse
  • We felt a bit disapointed
  • A solution : flip the problem

Summary

  • A novel progressive screen-space sampler
    • That distributes error as a blue-noise
    • State-of-the-art convergence
    • Efficient and compatible with real-time
  • Our contributions
    • New optimization space: test integrands
    • Two stage optimization: ranking and scrambling
  • Clearing misconceptions
    • No more Cranley Patterson rotations
    • Blue-noise not restricted to previz.
    • Only scratched the surface (see EGSR talk)

Thank you for your attention

paper
supp. mat.
code
available at belcour.github.io/blog