You are on page 1of 4

| VDR Follow Up – Grain and Fine Details

gpuopen.com/learn/vdr-follow-up-grain-and-fine-details/

The prior post in this series established a base technique for adding grain, and now this post is going to look
at very subtle changes to improve quality using the same 3-bit/channel quantization case from the prior
post. The first change is to apply a clamped amount of sharpening prior to adding the grain. Grain can have
a tendency to disrupt and mask fine detail. The sharpening is designed to increase the minimum contrast on
an edge to the point where it survives the addition of grain. The second change is to adjust the Probability
Density Function of the grain.

The image below to the left is a crop from the prior post: no sharpening and a Rectangular Probability
Density Function (RPDF). To the left: sharpening and something similar to a Gaussian Probability Density
Function (GPDF). The differences can be hard to spot, look in the upper right under the green leaves. The
fine detail is better preserved for the vines.

Sharpening
The sharpening is a simple 3×3 filter kernel applied to the linear color. The sharpening is limited so that the
maximum change is proportional to the amount of grain added to the image.

float3 sharpen; // added to color to sharpen the image


float1 limiter; // constant factor proportional to the amount of grain added
float3 maxSharpen = max3(abs(sharpen.r), abs(sharpen.g), abs(sharpen.b));
sharpen *= min(limiter, maxSharpen) / maxSharpen;

Probability Density Function


The prior post established a sorting technique to force a texture into a uniform or Rectangular Probability
Density Function. After building a RPDF texture, the PDF can be changed by taking the grain value which
ranges from {-1.0 to 1.0}, and applying a function to that value. Below I used f(x) = x*abs(x) to shape into

1/4
a Triangular Probability Density Function (TPDF). And then f(x) = (3.0*x*x - 2.0*x*x*abs(x)) * sign(x)
(aka smoothstep() in HLSL and GLSL ) to shape into something similar to a Gaussian Probability Density
Function.

First the result for the TPDF,

The TPDF reduces the amount of visible grain by reducing the probability of getting a grain value close to {-
1} or {1}. This has a positive visual side effect of increasing clarity for fine detail. However this appears to
have a non-energy conserving side effect as well: the quantized value is more likely to stay close to a band.
In the above image the lower right dark colors move closer to black (in an area which has no full black
values).

This is easier to see in the four cropped swatches below. From the left to right: {RPDF, GPDF proxy, TPDF,
and original image}.

2/4
The GPDR proxy below, implemented via the smoothstep() function, provides a middle ground which
does not exhibit as obvious tonal separation,

Practical Application

3/4
Regardless of application of grain/dithering in linear (this series) or after conversion to a non-linear output
space (alternative method), the aim of these techniques is to remove banding regardless of bit-depth and
maximize the visual quality of the pixel. These techniques applied temporally look substantially better.

Mikkel Gjoel’s dithering section of Playdead’s Low Complexity, High Fidelity – INSIDE Rendering talk at
GDC 2016 shows a great example of using grain mixed with temporal AA to remove banding while
maintaining cache friendly and lower bandwidth 32-bit/pixel color formats. See the first few seconds of
the Youtube Video.

4/4

You might also like