diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 93abd3575..ec00b3b89 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -580,6 +580,17 @@ enable_auto_exposure (Enable Automatic Exposure) bool false # Requires: shaders, enable_auto_exposure exposure_compensation (Exposure compensation) float 0.0 -1.0 1.0 +# Apply dithering to reduce color banding artifacts. +# Dithering significantly increases the size of losslessly-compressed +# screenshots and it works incorrectly if the display or operating system +# performs additional dithering or if the color channels are not quantized +# to 8 bits. +# With OpenGL ES, dithering only works if the shader supports high +# floating-point precision and it may have a higher performance impact. +# +# Requires: shaders +debanding (Enable Debanding) bool true + [**Bloom] # Set to true to enable bloom effect. diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 928e408e2..973cfc424 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -1,6 +1,13 @@ #define rendered texture0 #define bloom texture1 +#ifdef GL_ES +// Dithering requires sufficient floating-point precision +#ifndef GL_FRAGMENT_PRECISION_HIGH +#undef ENABLE_DITHERING +#endif +#endif + struct ExposureParams { float compensationFactor; }; @@ -79,6 +86,20 @@ vec3 applySaturation(vec3 color, float factor) } #endif +#ifdef ENABLE_DITHERING +// From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf +// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom) +// NOTE: `frag_coord` is in pixels (i.e. not normalized UV). +vec3 screen_space_dither(highp vec2 frag_coord) { + // Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR. + highp vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord)); + dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0)); + + // Subtract 0.5 to avoid slightly brightening the whole viewport. + return (dither.rgb - 0.5) / 255.0; +} +#endif + void main(void) { vec2 uv = varTexCoord.st; @@ -125,5 +146,10 @@ void main(void) // return to sRGB colorspace (approximate) color.rgb = pow(color.rgb, vec3(1.0 / 2.2)); +#ifdef ENABLE_DITHERING + // Apply dithering just before quantisation + color.rgb += screen_space_dither(gl_FragCoord.xy); +#endif + gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image. } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 0fcdebf60..3e6e67e45 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -767,6 +767,9 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, shaders_header << "#define SSAA_SCALE " << ssaa_scale << ".\n"; } + if (g_settings->getBool("debanding")) + shaders_header << "#define ENABLE_DITHERING 1\n"; + shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics std::string common_header = shaders_header.str(); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 6a48a9b40..d61a91d40 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -264,6 +264,7 @@ void set_default_settings() settings->setDefault("enable_waving_plants", "false"); settings->setDefault("exposure_compensation", "0.0"); settings->setDefault("enable_auto_exposure", "false"); + settings->setDefault("debanding", "true"); settings->setDefault("antialiasing", "none"); settings->setDefault("enable_bloom", "false"); settings->setDefault("enable_bloom_debug", "false"); @@ -499,6 +500,7 @@ void set_default_settings() settings->setDefault("active_block_range", "2"); settings->setDefault("viewing_range", "50"); settings->setDefault("leaves_style", "simple"); + settings->setDefault("debanding", "false"); settings->setDefault("curl_verify_cert", "false"); // Apply settings according to screen size