mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Introduce dithering to reduce banding (#4497)
This PR introduces dithering in the egui_glow and egui_wgpu backends to reduce banding artifacts. It's based on the approach mentioned in #4493 with the small difference that the amount of noise is scaled down slightly to avoid dithering colors that can be represented exactly. This keeps flat surfaces clean. Exaggerated dithering to show what is happening:  Subtle dithering as commited.  Closes #4493
This commit is contained in:
@@ -138,6 +138,7 @@ impl Painter {
|
||||
gl: Arc<glow::Context>,
|
||||
shader_prefix: &str,
|
||||
shader_version: Option<ShaderVersion>,
|
||||
dithering: bool,
|
||||
) -> Result<Self, PainterError> {
|
||||
crate::profile_function!();
|
||||
crate::check_for_gl_error_even_in_release!(&gl, "before Painter::new");
|
||||
@@ -197,9 +198,10 @@ impl Painter {
|
||||
&gl,
|
||||
glow::FRAGMENT_SHADER,
|
||||
&format!(
|
||||
"{}\n#define NEW_SHADER_INTERFACE {}\n#define SRGB_TEXTURES {}\n{}\n{}",
|
||||
"{}\n#define NEW_SHADER_INTERFACE {}\n#define DITHERING {}\n#define SRGB_TEXTURES {}\n{}\n{}",
|
||||
shader_version_declaration,
|
||||
shader_version.is_new_shader_interface() as i32,
|
||||
dithering as i32,
|
||||
srgb_textures as i32,
|
||||
shader_prefix,
|
||||
FRAG_SRC
|
||||
|
||||
@@ -16,6 +16,27 @@ uniform sampler2D u_sampler;
|
||||
varying vec2 v_tc;
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------
|
||||
// Adapted from
|
||||
// https://www.shadertoy.com/view/llVGzG
|
||||
// Originally presented in:
|
||||
// Jimenez 2014, "Next Generation Post-Processing in Call of Duty"
|
||||
//
|
||||
// A good overview can be found in
|
||||
// https://blog.demofox.org/2022/01/01/interleaved-gradient-noise-a-different-kind-of-low-discrepancy-sequence/
|
||||
// via https://github.com/rerun-io/rerun/
|
||||
float interleaved_gradient_noise(vec2 n) {
|
||||
float f = 0.06711056 * n.x + 0.00583715 * n.y;
|
||||
return fract(52.9829189 * fract(f));
|
||||
}
|
||||
|
||||
vec3 dither_interleaved(vec3 rgb, float levels) {
|
||||
float noise = interleaved_gradient_noise(gl_FragCoord.xy);
|
||||
// scale down the noise slightly to ensure flat colors aren't getting dithered
|
||||
noise = (noise - 0.5) * 0.95;
|
||||
return rgb + noise / (levels - 1.0);
|
||||
}
|
||||
|
||||
// 0-1 sRGB gamma from 0-1 linear
|
||||
vec3 srgb_gamma_from_linear(vec3 rgb) {
|
||||
bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
|
||||
@@ -37,5 +58,12 @@ void main() {
|
||||
#endif
|
||||
|
||||
// We multiply the colors in gamma space, because that's the only way to get text to look right.
|
||||
gl_FragColor = v_rgba_in_gamma * texture_in_gamma;
|
||||
vec4 frag_color_gamma = v_rgba_in_gamma * texture_in_gamma;
|
||||
|
||||
// Dither the float color down to eight bits to reduce banding.
|
||||
// This step is optional for egui backends.
|
||||
#if DITHERING
|
||||
frag_color_gamma.rgb = dither_interleaved(frag_color_gamma.rgb, 256.);
|
||||
#endif
|
||||
gl_FragColor = frag_color_gamma;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ impl EguiGlow {
|
||||
gl: std::sync::Arc<glow::Context>,
|
||||
shader_version: Option<ShaderVersion>,
|
||||
native_pixels_per_point: Option<f32>,
|
||||
dithering: bool,
|
||||
) -> Self {
|
||||
let painter = crate::Painter::new(gl, "", shader_version)
|
||||
let painter = crate::Painter::new(gl, "", shader_version, dithering)
|
||||
.map_err(|err| {
|
||||
log::error!("error occurred in initializing painter:\n{err}");
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user