Dither
Dithering trades smooth tonal range for spatial noise so an image can survive
quantization (8-bit output, low-bit colour palettes, alpha cut-outs) without
visible banding. Each function below returns a per-pixel threshold in
[0, 1) that you compare against your value before snapping it.
Pull one in by slug and apply it inline:
let main = r#"@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { let v = in.uv.x; // smooth source let t = bayer4x4(vec2<i32>(in.position.xy)); // threshold let q = step(t, v); // 1-bit dither return vec4<f32>(vec3<f32>(q), 1.0);}"#;let shader = Shader::new(&["dither/bayer4x4", main])?;bayer2x2
Section titled “bayer2x2”Ordered dither threshold in [0, 1) for an integer pixel coord.
fn bayer2x2(p: vec2<i32>) -> f32
bayer4x4
Section titled “bayer4x4”Ordered dither threshold in [0, 1) for an integer pixel coord.
fn bayer4x4(p: vec2<i32>) -> f32
bayer8x8
Section titled “bayer8x8”8x8 ordered dither threshold in [0, 1) for an integer pixel coord.
fn bayer8x8(p: vec2<i32>) -> f32
interleaved_gradient_noise
Section titled “interleaved_gradient_noise”Jorge Jimenez’s IGN — a fast, blue-noise-flavoured dither that breaks up banding without the regular grid look of Bayer matrices.
fn interleaved_gradient_noise(p: vec2<f32>) -> f32