Texture Dithering

Just a sample to demonstrate texture-based grayscale dithering over some FBM clouds

Created by marcogomez on Tue, 28 Sep 2021 15:07:05 GMT.


// ╔═════════════╦═════════════════╦════════════════╗
// ║ Marco Gomez ║ @TheCodeTherapy ║ https://mgz.me ║
// ╚═════════════╩═════════════════╩════════════════╝
precision highp float;

uniform sampler2D prgm2Texture;
uniform vec2 resolution;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm2 = texture2D(prgm2Texture, uv);
  gl_FragColor = prgm2;
}

// ╔═════════════╦═════════════════╦════════════════╗
// ║ Marco Gomez ║ @TheCodeTherapy ║ https://mgz.me ║
// ╚═════════════╩═════════════════╩════════════════╝
precision highp float;

uniform sampler2D noiseTexture;
uniform vec2 resolution;
uniform float time;
uniform float fft;

const float PI = acos(-1.0); // π or acos(-1.0) or 180°
const float TAU = PI * 2.0; //  τ = π * 2 or 360°
const mat2 m2 = mat2(0.80, -0.60, 0.60, 0.80);

float gaussian(float z, float u, float o) {
  return (
    1.0 / (o * sqrt(TAU))
  ) * exp(-(((z - u) * (z - u)) / (2.0 * (o * o))));
}

vec3 gaussGrain(float t) {
  vec2 ps = vec2(1.0) / resolution.xy;
  vec2 uv = gl_FragCoord.xy * ps;
  float seed = dot(uv, vec2(12.9898, 78.233));
  float noise = fract(sin(seed) * 43758.5453 + t);
  noise = gaussian(noise, 0.0, 0.5);
  return vec3(noise);
}

float noise(vec2 x) {
  vec2 p = floor(x);
  vec2 f = fract(x);
  f = f * f * (3.0 - 2.0 * f);
  float a = texture2D(noiseTexture, (p + vec2(0.5, 0.5 + fft * 0.25)) / 256.0, 0.0).x;
  float b = texture2D(noiseTexture, (p + vec2(1.5, 0.5 + fft * 0.25)) / 256.0, 0.0).x;
  float c = texture2D(noiseTexture, (p + vec2(0.5, 1.5 + fft * 0.25)) / 256.0, 0.0).x;
  float d = texture2D(noiseTexture, (p + vec2(1.5, 1.5 + fft * 0.25)) / 256.0, 0.0).x;
  return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

float fbm (vec2 uv) {
  float f = 0.0;
  f += 0.5000 * noise(uv); uv = m2 * uv * 2.02;
  f += 0.2500 * noise(uv); uv = m2 * uv * 2.03;
  f += 0.1250 * noise(uv); uv = m2 * uv * 2.01;
  f += 0.0625 * noise(uv);
  return f / 0.9375;
}

vec3 clouds(vec2 uv) {
  vec3 color;
  float t = time * 0.75;
  vec2 q = vec2( 0.0 ), r = vec2( 0.0 );
  q.x = fbm(uv + t * 0.25);
  q.y = fbm(uv + vec2(1.0));
  r.x = fbm(uv + q + vec2(1.7, 9.2) + 0.31 * t * 0.25);
  r.y = fbm(uv + q + vec2(8.3, 2.8) + 0.21 * t * 0.25);
  float f = fbm(uv + r);
  color = mix(vec3(1.0, 1.0, 1.0), vec3(0.3, 1.6, 1.6), clamp((f * f) * 2.0, 0.0, 1.0) + fft * 0.5);
  color = mix(color, vec3(0.4, 0.2, 0.16), clamp(length(q) + fft * 0.33, 0.0, 1.0));
  color = mix(color, vec3(0.4, 0.7, 3.00), clamp(length(r.x) + fft * 0.33, 0.0, 1.0));
  return color * color * color;
}

vec2 curve(vec2 uv) {
  uv *= 1.1;
  uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);
  uv.y *= 1.0 + pow((abs(uv.x) / 5.0), 2.0);
  uv = (uv / 2.0) + 0.5;
  uv = uv * 0.92 + 0.08;
  return uv;
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.y;
  uv.y *= (resolution.x / resolution.y) * -.0625 - 1.0;
  vec2 p = -1.0 + 2.0 * uv;
  p.x -= time * 0.0125;
  vec3 g = gaussGrain(time) * 0.04;
  vec3 col = clouds(p * 3.0) - g;
  gl_FragColor = vec4(col, 1.0);
}

// ╔═════════════╦═════════════════╦════════════════╗
// ║ Marco Gomez ║ @TheCodeTherapy ║ https://mgz.me ║
// ╚═════════════╩═════════════════╩════════════════╝
precision highp float;

uniform sampler2D ditherTexture;
uniform sampler2D prgm1Texture;
uniform vec2 resolution;
uniform float time;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  float pixelSize = 4.0;

  vec2 pPixel = floor(gl_FragCoord.xy / pixelSize);
  vec2 pRes = floor(resolution.xy / pixelSize);
  vec2 pUV = pPixel / pRes;
  vec4 color = texture2D(prgm1Texture, pUV);
  float alpha = 1.0;

  vec2 tuv = gl_FragCoord.xy / 8.0;
  tuv *= 17.0;
  tuv = fract(tuv);
  vec4 tdither = texture2D(ditherTexture, tuv);
  vec4 lum = vec4(0.299, 0.587, 0.114, 0);
  float dither = dot(tdither, lum);
  float gs = dot(color, lum) * 0.3;
  gs = (gs - 0.5) * 1.2 + 0.5;
  vec3 col = vec3(step(dither, gs));
  gl_FragColor = vec4(col, alpha);
}