Skip to content

Commit

Permalink
Add dithering
Browse files Browse the repository at this point in the history
On some displays, banding is easily noticeable in Minetest.
Dithering before the quantisation to 8 bits can prevent banding artifacts
and an implementation requires only a few lines of code.

Dithering is disabled by default and can be enabled with a setting.
It is applied in the second-stage shader and uses the algorithm supplied by Calinou.
  • Loading branch information
HybridDog committed Oct 22, 2023
1 parent c9655e5 commit 49dec40
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 0 deletions.
9 changes: 9 additions & 0 deletions builtin/settingtypes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,15 @@ 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 colour 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 colour channels are not quantized
# to 8 bits. Therefore it is disabled by default.
#
# Requires: shaders
dithering (Enable Dithering) bool false

[**Bloom]

# Set to true to enable bloom effect.
Expand Down
19 changes: 19 additions & 0 deletions client/shaders/second_stage/opengl_fragment.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,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(vec2 frag_coord) {
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
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;
Expand Down Expand Up @@ -125,5 +139,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.
}
9 changes: 9 additions & 0 deletions minetest.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,15 @@
# type: float min: -1 max: 1
# exposure_compensation = 0.0

# Apply dithering to reduce colour 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 colour channels are not quantized
# to 8 bits. Therefore it is disabled by default.
# type: bool
# dithering = false


### Bloom

# Set to true to enable bloom effect.
Expand Down
3 changes: 3 additions & 0 deletions src/client/shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,9 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaders_header << "#define SSAA_SCALE " << ssaa_scale << ".\n";
}

if (g_settings->getBool("dithering"))
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();
Expand Down

0 comments on commit 49dec40

Please sign in to comment.