Lessons Learned and Improvements
when Building Screen-Space Samplers
with Blue-Noise Error Distribution

Laurent Belcour

Eric Heitz

⚠️ What You See Is Maybe Not What You Should ⚠️

Two Years Ago ...

  • Blue-noise screen-space sampler

LittlestTokyo - 4 spp

Random Scrambling

Heitz et al. [2019]

Random Scrambling

Heitz et al. [2019]

3D model by Glen Fox

Inset
Power Spectrum

Two Years Ago ...

Two Years Ago ...


Two Years Ago ...

  • Blue-noise screen-space sampler
  • Please refer to our 2019 talk
  • Many people seems to like it
    • Already in Unity's HDRP and Lightmapper
    • A lot of feedbacks and misunderstandings

Unity's Lightmapper

Viking - 32 spp

Heitz et al. [2019]

Ours

Heitz et al. [2019]

Ours

3D model by nigelgoh

Inset
Power Spectrum

First A Quick Recap


Scrambling mask


Sorting mask
Sobol samples
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

Optimized on test integrands

See [Heitz et al. 2019] for more details

First A Quick Recap

Test integrand

First A Quick Recap

Test integrand

Scrambling mask


Sorting mask
Test 'rendering'
Misunderstanding #1

👨‍💻
I bet the blue-noise won't work on smooth integrands since it is optimized for discontinuities.
@pedanticMathematician - very late in the night.

Insight #1: Optimizing Vectors

Test 'rendering'

Insight #1: Optimizing Vectors

Test 'rendering'
=
$$\begin{bmatrix} \\ \vdots \\ \vdots \\ \\ \end{bmatrix}$$
Vector of realization

Insight #1: Optimizing Vectors

  • We are swapping 216D vectors!
    • Requires a specific loss
  • Equivalent to swapping PCA coordinates
  • Eigen-test integrands from PCA
    • They are quite smooth!
$$\begin{bmatrix} \\ \vdots \\ \vdots \\ \\ \end{bmatrix} = R_\theta \begin{bmatrix} \\ \vdots \\ \vdots \\ \\ \end{bmatrix}$$
$$ f_3(x)$$
$$ f_{49}(x)$$
Sobol + XOR
$$ f_0 $$
$$ f_1 $$
$$ f_2 $$
$$ f_3 $$
$$ f_4 $$

The Limits of Owen's Scrambled Sobol

  • But not that smooth ...
Sobol + XOR
$$ f_0 $$
$$ f_1 $$
$$ f_2 $$
$$ f_3 $$
$$ f_4 $$
independent
$$ f_0 $$
$$ f_1 $$
$$ f_2 $$
$$ f_3 $$
$$ f_4 $$

The Limits of Owen's Scrambled Sobol

  • But not that smooth ...
    • Impacts the quality of the blue-noise
Sobol+XOR
Independent

The Limits of Owen's Scrambled Sobol

  • But not that smooth ...
    • Impacts the quality of the blue-noise
  • Difference seems due to scrambling
    • Sobol+XOR produces few variations
    • Independent produces an infinite set sequences
  • Works with a different scrambling
    • Wait! Cranley-Patterson Rotations???
Sobol+XOR
✔ convergence
✘ blue-noise
Independent
✘ convergence
✔ blue-noise
Sobol+CPR
✘ convergence
✔ blue-noise
Question #1

Can I use my own sequence?
@QMC_Master - mod(noon * k, 1).

Changing The Sequence

Changing The Sequence

  • Rank-1 Lattices [Keller 2019]
    • Wrapped 1D points
    • Empirically do no alter convergence
  • But provides better results
    • Large space of variations




Rendering of Teapot at 16 spp
Request #1

👩‍💻
Plz share the optimizer code!
@SpeedyCoder - 2s after publishing the talk.

Old Optimizer

  • A Genetic Algorithm
    • Swap random scrambles according to loss
    • Extremely slow to converge!

GPU-friendly Optimizer

  • Start with almost perfect mask
    • Only one pixel to move
    • We only need to randomly find it ...
  • Can we try all possible swap?
    • We need a collisions-free scheme
    • We need to preserve neighbourhoods

Dither tile

Collision Free Scheme


Dither tile
scramble pairs

Linearized and permuted dither tile
{
{
{
{
{
{
{
{

Preserving Neighbourhoods

  • Still not optimal w.r.t the loss
    • Permutation might break neighboors
  • Not a big deal
    • Solution: only permute a half of the pairs
    • We even got simulated annealing for free
  • Allows quick iteration
    • ~9 times faster than our CPU-sequential version
  • Available for all!
    • Check our projet webpage
    • Implementation by Sylvain Durand

Dither tile
Question #F

👩‍🎨
How can I know if my tile is of good quality?
@QA - too often.

Quality Criterion

  • Assessing blue-noise quality is perceptual
    • Depends on the medium (screen, printed paper, ...)
    • Depends on the conditions (resolution, ...)
    • We need a numerical criterion

Quality Criterion

  • Assessing blue-noise quality is perceptual
    • Depends on the medium (screen, printed paper, ...)
    • Depends on the conditions (resolution, ...)
    • We need a numerical criterion
  • The idea is to look at denoising [Chizhov et al. 2020]
    • Blue-noise improves L2 after denoising
    • But how can we make it scene agnostic?

Blur Criterion on Test Integrands

integrated test function at 8 spp

error after blurring

Blur Criterion on Test Integrands

Is Stratification the Ultimate Goal?

  • Dyadicity is a hot topic
  • Its only about per pixel convergence
    • Misses convergence after denoising
    • Maybe not so important?
Request #2

🧑‍🎓
It tried it using BDPT, it doesn't work! Can you debug my code?
@GeekyStudent - 5min before sending his homework.

Use Your Sampler Wisely

  • BDPT: Dimensions must match
    • Sub-paths must share random numbers
    • Not vanilla!

More Please!

  • More dimensions
    • Not best quality for direct illumination
    • Takes time to optimize
  • We advise to pad dimensions
    • We only optimize 2D tiles
    • Pad with random tile shifts
  • More samples
    • Tune your sampler to you SPPs!

Summary

  • We improved our 2019 screen-space sampler
    • Better blue-noise of rendered frames
    • Better quality after denoising
    • Still efficient and compatible with real-time
  • Our contributions
    • New insights on the optimization process
    • Changed the sequence and scrambling
    • Accelerated optimization using GPU
  • Thanks to you
    • Interaction with a lot of interested folks
Project available at belcour.github.io/blog