Skip to content
Snippets Groups Projects
Unverified Commit 071ea161 authored by Danny Lin's avatar Danny Lin Committed by Simão Gomes Viana
Browse files

blur: Dither output using triangular RGB blue noise

Blurring images usually creates a lot of gradual color transitions,
especially at higher radii. When the output is quantized to 8-bit RGB
(8 bpc / 24 bpp) for display, the lost information (quantization error)
results in visible banding. This is most apparent in scenes that are
predominantly grayscale because of the reduced color fidelity, but it
can still be an issue in scenes with many colors.

To fix the banding, this commit dithers the blur output during
quantization. This is done in the final output mixing step because
dithering works best when it operates on each individual pixel;
upscaling a dithered image will not help much.

Error diffusion dithering is ideal, but it is not practical for GPU
fragment shaders because it requires processing each pixel sequentually.
Instead, we use ordered dithering, which tiles a pattern across the
entire image to influence rounding during quantization.

The most visually-appealing pattern is used for ordered dithering: blue
noise [1]. Other patterns considered:
  - Bayer matrix: visible pattern in output
  - White (random) noise: high-frequency components are distracting and
    make tiling repetitions apparent
  - Interleaved gradient noise, generated in shader [2]: aliasing
    artifacts, somewhat visible pattern, and expensive ALU operations
  - Oculus Dither17 pattern, generated in shader [3]: same issues as
    interleaved gradient noise
Blue noise contains very few low-frequency components, making it ideal
for dithering because it tiles seamlessly and does not distract from the
actual image.

When dithering, the blue noise is reshaped from uniform distribution to
a triangular PDF distribution. This makes the noise appear more uniform
when used for dithering: instead of some areas having less visible noise
than others, the noise appears to be evenly spread across the entire
image [4] [5]. A naïve implementation of triangular reshaping is
relatively expensive (+60 µs), but using an optimized implementation of
sign() reduces the cost to ~30 µs [6].

Finally, to avoid adding noise to pure black (#000000) images or
affecting saturation, the dithering implementation needs to perform
gamma correction [5]. The real sRGB transfer function is relatively
expensive because it's a piecewise function with linear and exponential
(gamma 2.4) parts that average to gamma 2.2, so we approximate it
instead with gamma 2. This makes the performance cost of gamma
correction negligible while still producing acceptable results.

Most applications of dithering add up to 1x LSB (least significant bit -
i.e. 1/256 for 8-bpc output) of noise, but this implementation adds up
to 4x LSB (i.e. 1/64 for 8-bpc) of noise. While this adds more noise to
the output, it was empirically determined to be more effective for
reducing banding in color gradients than 1x LSB. There are still nearly
no visible noise artifacts when using blue noise and gamma correction.

Note that dithering requires the blurred image to be rendered at 10-bit
HDR (10 bpc / 30 bpp) internally; otherwise, it would just add noise
without fixing banding.

This increases rendering time by ~300 µs on an Adreno 640 GPU at
1440x3040 resolution, which is a worthwhile tradeoff for the
significant improvement in quality.

[1] http://momentsingraphics.de/BlueNoise.html
[2] http://www.iryoku.com/downloads/Next-Generation-Post-Processing-in-Call-of-Duty-Advanced-Warfare-v18.pptx
[3] https://developer.oculus.com/blog/tech-note-shader-snippets-for-efficient-2d-dithering/
[4] https://loopit.dk/banding_in_games.pdf
[5] https://loopit.dk/rendering_inside.pdf
[6] https://twitter.com/SebAaltonen/status/878250919879639040



Change-Id: I80559654a19c6cc6f2f53c94b64963d0bb888af5
Signed-off-by: default avatarMohammad Hasan Keramat J <ikeramat@protonmail.com>
Signed-off-by: default avatarSimão Gomes Viana <devel@superboring.dev>
parent 43710493
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment