Files
filament/shaders/src/dithering.fs
Pixelflinger e31ebb7a82 Fix a few issues with dithering
- only triangular noise needs to be scaled between +/-1, other noises
  have a uniform distribution and need to be scaled between +/-0.5

- all dither routines work in RGBA

- fixed FXAA in opaque mode when dithering modified the alpha channel
  (which is used by FXAA). This fixes flickering when FXAA and dithering
  was enabled.

- use triangular noise dithering on mobile and desktop. The cost in
  not measurable on a pixel 4 / 1080p, and the quality is better.

- refactor dithering code a bit such that:
  - noise methods are not temporal
  - all dither functions have the same structure
2020-05-13 17:16:58 -07:00

118 lines
3.9 KiB
GLSL

//------------------------------------------------------------------------------
// Dithering configuration
//------------------------------------------------------------------------------
// Dithering operators
#define DITHERING_NONE 0
#define DITHERING_INTERLEAVED_NOISE 1
#define DITHERING_VLACHOS 2
#define DITHERING_TRIANGLE_NOISE 3
#define DITHERING_TRIANGLE_NOISE_RGB 4
#if defined(TARGET_MOBILE)
#define DITHERING_OPERATOR DITHERING_TRIANGLE_NOISE
#else
#define DITHERING_OPERATOR DITHERING_TRIANGLE_NOISE
#endif
//------------------------------------------------------------------------------
// Noise
//------------------------------------------------------------------------------
// n must be normalized in [0..1] (e.g. texture coordinates)
float triangleNoise(highp vec2 n) {
// triangle noise, in [-1.0..1.0[ range
n = fract(n * vec2(5.3987, 5.4421));
n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
highp float xy = n.x * n.y;
// compute in [0..2[ and remap to [-1.0..1.0[
return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
}
// n must not be normalize (e.g. window coordinates)
float interleavedGradientNoise(highp vec2 n) {
return fract(52.982919 * fract(dot(vec2(0.06711, 0.00584), n)));
}
//------------------------------------------------------------------------------
// Dithering
//------------------------------------------------------------------------------
vec4 Dither_InterleavedGradientNoise(vec4 rgba) {
// Jimenez 2014, "Next Generation Post-Processing in Call of Duty"
highp vec2 uv = gl_FragCoord.xy;
uv += frameUniforms.time;
// The noise variable must be highp to workaround Adreno bug #1096.
highp float noise = interleavedGradientNoise(uv);
// remap from [0..1[ to [-0.5..0.5[
noise -= 0.5;
return rgba + vec4(noise / 255.0);
}
vec4 Dither_TriangleNoise(vec4 rgba) {
// Gjøl 2016, "Banding in Games: A Noisy Rant"
highp vec2 uv = gl_FragCoord.xy * frameUniforms.resolution.zw;
uv += vec2(0.07 * fract(frameUniforms.time));
// The noise variable must be highp to workaround Adreno bug #1096.
highp float noise = triangleNoise(uv);
// noise is in [-1..1[
return rgba + vec4(noise / 255.0);
}
vec4 Dither_Vlachos(vec4 rgba) {
// Vlachos 2016, "Advanced VR Rendering"
float noise = dot(vec2(171.0, 231.0), gl_FragCoord.xy + frameUniforms.time);
vec3 noiseRGB = fract(vec3(noise) / vec3(103.0, 71.0, 97.0));
// remap from [0..1[ to [-0.5..0.5[
noiseRGB -= 0.5;
return vec4(rgba.rgb + (noiseRGB / 255.0), rgba.a);
}
vec4 Dither_TriangleNoiseRGB(vec4 rgba) {
// Gjøl 2016, "Banding in Games: A Noisy Rant"
highp vec2 uv = gl_FragCoord.xy * frameUniforms.resolution.zw;
uv += vec2(0.07 * fract(frameUniforms.time));
vec3 noiseRGB = vec3(
triangleNoise(uv),
triangleNoise(uv + 0.1337),
triangleNoise(uv + 0.3141));
// noise is in [-1..1[
return rgba + noiseRGB.xyzx / 255.0;
}
//------------------------------------------------------------------------------
// Dithering dispatch
//------------------------------------------------------------------------------
/**
* Dithers the specified RGBA color based on the current time and fragment
* coordinates the input must be in the final color space (including OECF).
* This dithering function assumes we are dithering to an 8-bit target.
* This function dithers the alpha channel assuming premultiplied output
*/
vec4 dither(vec4 rgba) {
#if DITHERING_OPERATOR == DITHERING_NONE
return rgba;
#elif DITHERING_OPERATOR == DITHERING_INTERLEAVED_NOISE
return Dither_InterleavedGradientNoise(rgba);
#elif DITHERING_OPERATOR == DITHERING_VLACHOS
return Dither_Vlachos(rgba);
#elif DITHERING_OPERATOR == DITHERING_TRIANGLE_NOISE
return Dither_TriangleNoise(rgba);
#elif DITHERING_OPERATOR == DITHERING_TRIANGLE_NOISE_RGB
return Dither_TriangleNoiseRGB(rgba);
#endif
}