64 Colors Are Enough

Some plasma-like playground to prove that 64 colors are more than enough if you have an open mind and a good heart.

Created by marcogomez on Thu, 27 May 2021 22:04:25 GMT.


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

uniform sampler2D prgm3Texture;
uniform vec2 resolution;
uniform float time;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm3 = texture2D(prgm3Texture, uv);
  gl_FragColor = vec4(prgm3.xyz, 1.0);
}

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

uniform vec2 resolution;
uniform float time;

float modulate(vec3 p) {
  p.y += 1.5 * sin(p.x + time);
  p.z += 1.0 * time;
  return length(0.2 * sin(p.x / 2.0 + p.y) + cos(p.zzx / 4.0)) - 0.8;
}

vec2 rotate(vec2 uv, float a) {
  float s = sin(a); float c = cos(a);
  mat2 m = mat2(+c, -s, +s, +c);
  return m * uv;
}

void main(void) {
  vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;
  float ar = resolution.x / resolution.y;
  uv.x *= ar;
  vec3 col = 0.5 - vec3(uv, -0.4);
  vec3 orig = col;
  for (int i = 0; i < 48; i++) {
    vec3 c = vec3(col.x, col.y, col.z);
    c.xy = rotate(c.xy, time * -0.1);
    orig += modulate(orig) * c;
  }
  vec3 c = abs(
    modulate(orig * 1.8) * vec3(0.3 + sin(orig.x) / 3.0, 0.1, 0.1) +
    modulate(orig * 0.5) * vec3(0.1, 0.12, 0.15)
  ) * (5.0 - orig.y / 2.0) * 0.5;
  c = clamp(c, 0.0, 1.0);
  c.rgb += abs(
    modulate(orig + col) * vec3(0.30, 0.15, 0.10) +
    modulate(orig * 0.5) * vec3(0.10, 0.05, 0.00)
  ) * (7.0 - orig.x / 3.0) * 0.5;
  c = clamp(c * 2.0, 0.0, 1.0);
  gl_FragColor = vec4(clamp(c * c, 0.0, 1.0), 1.0);
}

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

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

#define try(new) old = mix (new, old, step (length (old-ref), length (new-ref)));
vec3 findClosest(vec3 ref) {
  vec3 old = vec3 (100.0 * 255.0);
  try(vec3(  0.0,   0.0,   0.0)); //  0 - black           (YPbPr = 0.0  ,  0.0 ,  0.0 )
  try(vec3(133.0,  59.0,  81.0)); //  1 - magenta         (YPbPr = 0.25 ,  0.0 ,  0.5 )
  try(vec3( 80.0,  71.0, 137.0)); //  2 - dark blue       (YPbPr = 0.25 ,  0.5 ,  0.0 )
  try(vec3(233.0,  93.0, 240.0)); //  3 - purple          (YPbPr = 0.5  ,  1.0 ,  1.0 )
  try(vec3(  0.0, 104.0,  82.0)); //  4 - dark green      (YPbPr = 0.25 ,  0.0 , -0.5 )
  try(vec3(146.0, 146.0, 146.0)); //  5 - gray #1         (YPbPr = 0.5  ,  0.0 ,  0.0 )
  try(vec3(  0.0, 168.0, 241.0)); //  6 - medium blue     (YPbPr = 0.5  ,  1.0 , -1.0 )
  try(vec3(202.0, 195.0, 248.0)); //  7 - light blue      (YPbPr = 0.75 ,  0.5 ,  0.0 )
  try(vec3( 81.0,  92.0,  15.0)); //  8 - brown           (YPbPr = 0.25 , -0.5 ,  0.0 )
  try(vec3(235.0, 127.0,  35.0)); //  9 - orange          (YPbPr = 0.5  , -1.0 ,  1.0 )
  try(vec3(241.0, 166.0, 191.0)); // 11 - pink            (YPbPr = 0.75 ,  0.0 ,  0.5 )
  try(vec3(  0.0, 201.0,  41.0)); // 12 - green           (YPbPr = 0.5  , -1.0 , -1.0 )
  try(vec3(203.0, 211.0, 155.0)); // 13 - yellow          (YPbPr = 0.75 , -0.5 ,  0.0 )
  try(vec3(154.0, 220.0, 203.0)); // 14 - aqua            (YPbPr = 0.75 ,  0.0 , -0.5 )
  try(vec3(255.0, 255.0, 255.0)); // 15 - white           (YPbPr = 1.0  ,  0.0 ,  0.0 )
  try(vec3(120.0,  41.0,  34.0)); //  2 - red             (YPbPr = 0.25 , -0.383 ,  0.924 )
  try(vec3(135.0, 214.0, 221.0)); //  3 - cyan            (YPbPr = 0.75 ,  0.383 , -0.924 )
  try(vec3(170.0,  95.0, 182.0)); //  4 - purple          (YPbPr = 0.5  ,  0.707 ,  0.707 )
  try(vec3( 85.0, 160.0,  73.0)); //  5 - green           (YPbPr = 0.5  , -0.707 , -0.707 )
  try(vec3( 64.0,  49.0, 141.0)); //  6 - blue            (YPbPr = 0.25 ,  1.0   ,  0.0   )
  try(vec3(191.0, 206.0, 114.0)); //  7 - yellow          (YPbPr = 0.75 , -1.0   ,  0.0   )
  try(vec3(170.0, 116.0,  73.0)); //  8 - orange          (YPbPr = 0.5  , -0.707 ,  0.707 )
  try(vec3(234.0, 180.0, 137.0)); //  9 - light orange    (YPbPr = 0.75 , -0.707 ,  0.707 )
  try(vec3(184.0, 105.0,  98.0)); // 10 - light red       (YPbPr = 0.5  , -0.383 ,  0.924 )
  try(vec3(199.0, 255.0, 255.0)); // 11 - light cyan      (YPbPr = 1.0  ,  0.383 , -0.924 )
  try(vec3(234.0, 159.0, 246.0)); // 12 - light purple    (YPbPr = 0.75 ,  0.707 ,  0.707 )
  try(vec3(148.0, 224.0, 137.0)); // 13 - light green     (YPbPr = 0.75 , -0.707 , -0.707 )
  try(vec3(128.0, 113.0, 204.0)); // 14 - light blue      (YPbPr = 0.5  ,  1.0   ,  0.0   )
  try(vec3(255.0, 255.0, 178.0)); // 15 - light yellow    (YPbPr = 1.0  , -1.0   ,  0.0   )
  try(vec3(161.0,  77.0,  67.0)); //  2 - red             (YPbPr = 0.313 , -0.383 ,  0.924 )
  try(vec3(106.0, 193.0, 200.0)); //  3 - cyan            (YPbPr = 0.625 ,  0.383 , -0.924 )
  try(vec3(162.0,  86.0, 165.0)); //  4 - purple          (YPbPr = 0.375 ,  0.707 ,  0.707 )
  try(vec3( 92.0, 173.0,  95.0)); //  5 - green           (YPbPr = 0.5   , -0.707 , -0.707 )
  try(vec3( 79.0,  68.0, 156.0)); //  6 - blue            (YPbPr = 0.25  ,  1.0   ,  0.0   )
  try(vec3(203.0, 214.0, 137.0)); //  7 - yellow          (YPbPr = 0.75  , -1.0   ,  0.0   )
  try(vec3(163.0, 104.0,  58.0)); //  8 - orange          (YPbPr = 0.375 , -0.707 ,  0.707 )
  try(vec3(110.0,  83.0,  11.0)); //  9 - brown           (YPbPr = 0.25  , -0.924 ,  0.383 )
  try(vec3(204.0, 127.0, 118.0)); // 10 - light red       (YPbPr = 0.5   , -0.383 ,  0.924 )
  try(vec3( 99.0,  99.0,  99.0)); // 11 - dark grey       (YPbPr = 0.313 ,  0.0   ,  0.0   )
  try(vec3(139.0, 139.0, 139.0)); // 12 - grey            (YPbPr = 0.469 ,  0.0   ,  0.0   )
  try(vec3(155.0, 227.0, 157.0)); // 13 - light green     (YPbPr = 0.75  , -0.707 , -0.707 )
  try(vec3(138.0, 127.0, 205.0)); // 14 - light blue      (YPbPr = 0.469 ,  1.0   ,  0.0   )
  try(vec3(175.0, 175.0, 175.0)); // 15 - light grey      (YPbPr = 0.625  , 0.0   ,  0.0   )
  try(vec3( 62.0, 184.0,  73.0)); //  2 - medium green    (YPbPr = 0.53 , -0.509 , -0.755 )
  try(vec3(116.0, 208.0, 125.0)); //  3 - light green     (YPbPr = 0.67 , -0.377 , -0.566 )
  try(vec3( 89.0,  85.0, 224.0)); //  4 - dark blue       (YPbPr = 0.40 ,  1.0   , -0.132 )
  try(vec3(128.0, 128.0, 241.0)); //  5 - light blue      (YPbPr = 0.53 ,  0.868 , -0.075 )
  try(vec3(185.0,  94.0,  81.0)); //  6 - dark red        (YPbPr = 0.47 , -0.321 ,  0.679 )
  try(vec3(101.0, 219.0, 239.0)); //  7 - cyan            (YPbPr = 0.73 ,  0.434 , -0.887 )
  try(vec3(219.0, 101.0,  89.0)); //  8 - medium red      (YPbPr = 0.53 , -0.377 ,  0.868 )
  try(vec3(255.0, 137.0, 125.0)); //  9 - light red       (YPbPr = 0.67 , -0.377 ,  0.868 )
  try(vec3(204.0, 195.0,  94.0)); // 10 - dark yellow     (YPbPr = 0.73 , -0.755 ,  0.189 )
  try(vec3(222.0, 208.0, 135.0)); // 11 - light yellow    (YPbPr = 0.80 , -0.566 ,  0.189 )
  try(vec3( 58.0, 162.0,  65.0)); // 12 - dark green      (YPbPr = 0.47 , -0.453 , -0.642 )
  try(vec3(183.0, 102.0, 181.0)); // 13 - magenta         (YPbPr = 0.53 ,  0.377 ,  0.491 )
  try(vec3(204.0, 204.0, 204.0)); // 14 - grey            (YPbPr = 0.80 ,  0.0   ,  0.0   )
  try(vec3(226.0, 226.0, 226.0)); // L90
  try(vec3(198.0, 198.0, 198.0)); // L80
  try(vec3(171.0, 171.0, 171.0)); // L70
  try(vec3(145.0, 145.0, 145.0)); // L60
  try(vec3(119.0, 119.0, 119.0)); // L50
  try(vec3( 94.0,  94.0,  94.0)); // L40
  try(vec3( 71.0,  71.0,  71.0)); // L30
  try(vec3( 48.0,  48.0,  48.0)); // L20
  try(vec3( 27.0,  27.0,  27.0)); // L10
  return old ;
}

float ditherMatrix(float x, float y) {
  return (
    mix(
      mix(
        mix(
          mix(
            mix(mix(0.0,32.0,step(1.0,y)), mix(8.0,40.0,step(3.0,y)), step(2.0,y)),
            mix(mix(2.0,34.0,step(5.0,y)), mix(10.0,42.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          mix(
            mix(mix(48.0,16.0,step(1.0,y)), mix(56.0,24.0,step(3.0,y)), step(2.0,y)),
            mix(mix(50.0,18.0,step(5.0,y)), mix(58.0,26.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)),step(1.0,x)
        ),
        mix(
          mix(
            mix(mix(12.0,44.0,step(1.0,y)), mix(4.0,36.0, step(3.0,y)),step(2.0,y)),
            mix(mix(14.0,46.0,step(5.0,y)), mix(6.0,38.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          mix(
            mix(mix(60.0,28.0,step(1.0,y)), mix(52.0,20.0,step(3.0,y)), step(2.0,y)),
            mix(mix(62.0,30.0,step(5.0,y)), mix(54.0,22.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          step(3.0,x)
        ),
        step(2.0,x)
      ),
      mix(
        mix(
          mix(
            mix(mix(3.0,35.0,step(1.0,y)), mix(11.0,43.0, step(3.0,y)), step(2.0,y)),
            mix(mix(1.0,33.0,step(5.0,y)), mix(9.0,41.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          mix(
            mix(mix(51.0,19.0,step(1.0,y)), mix(59.0,27.0, step(3.0,y)), step(2.0,y)),
            mix(mix(49.0,17.0,step(5.0,y)), mix(57.0,25.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          step(5.0,x)
        ),
        mix(
          mix(
            mix(mix(15.0,47.0,step(1.0,y)), mix(7.0,39.0,step(3.0,y)), step(2.0,y)),
            mix(mix(13.0,45.0,step(5.0,y)), mix(5.0,37.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          mix(
            mix(mix(63.0,31.0,step(1.0,y)), mix(55.0,23.0, step(3.0,y)), step(2.0,y)),
            mix(mix(61.0,29.0,step(5.0,y)), mix(53.0,21.0,step(7.0,y)), step(6.0,y)),
            step(4.0,y)
          ),
          step(7.0,x)
        ),
        step(6.0,x)
      ),
      step(4.0,x)
    )
  );
}

vec3 dither(vec3 col, vec2 uv) {
  col *= 255.0;
  col += ditherMatrix(mod(uv.x, 8.0), mod(uv.y, 8.0));
  col = findClosest(clamp(col, 0.0, 255.0));
  return col / 255.0;
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm1 = texture2D(prgm1Texture, uv);
  prgm1.xyz = dither(prgm1.xyz, gl_FragCoord.xy * 0.25);
  gl_FragColor = vec4(prgm1.xyz, 1.0);
}

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

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

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float W = 1.2;
const float T = 7.5;

float filmicReinhardCurve(float x) {
  float q = (T * T + 1.0) * x * x;
  return q / (q + x + T * T);
}

vec3 filmicReinhard(vec3 c) {
  float w = filmicReinhardCurve(W);
  return vec3(
    filmicReinhardCurve(c.r),
    filmicReinhardCurve(c.g),
    filmicReinhardCurve(c.b)
  ) / w;
}

vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con) {
  const float AvgLumR = 0.5;
  const float AvgLumG = 0.5;
  const float AvgLumB = 0.5;
  const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
  vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
  vec3 brtColor = color * brt;
  vec3 intensity = vec3(dot(brtColor, LumCoeff));
  vec3 satColor = mix(intensity, brtColor, sat);
  vec3 conColor = mix(AvgLumin, satColor, con);
  return conColor;
}

float sdSquare(vec2 point, float width) {
  vec2 d = abs(point) - width;
  return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}

float vignette(vec2 uv, vec2 size, float roundness, float smoothness) {
  uv -= 0.5;
  float minWidth = min(size.x, size.y);
  uv.x = sign(uv.x) * clamp(abs(uv.x) - abs(minWidth - size.x), 0.0, 1.0);
  uv.y = sign(uv.y) * clamp(abs(uv.y) - abs(minWidth - size.y), 0.0, 1.0);
  float boxSize = minWidth * (1.0 - roundness);
  float dist = sdSquare(uv, boxSize) - (minWidth * roundness);
  return 1.0 - smoothstep(0.0, smoothness, dist);
}

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.5453123 + t);
  noise = gaussian(noise, 0.0, 0.5);
  return vec3(noise);
}

void main(void) {
  const float amount = 1.0;
  const float saturation = 0.7;
  vec2 vignetteSize = vec2(0.25, 0.25) * 1.0 - fft * 0.1;
  float vignetteRoundness = 0.12 + fft * 0.5;
  const float vignetteMix = 0.5;
  const float vignetteSmoothness = 0.5;
  const float reinhardAmount = 1.2;
  const float contrast = 0.9;
  const float brightness = 1.2;

  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm2 = texture2D(prgm2Texture, uv);
  vec3 reinhard = filmicReinhard(prgm2.rgb);
  vec3 color = prgm2.rgb;
  color = mix(prgm2.rgb, reinhard, reinhardAmount);
  color = ContrastSaturationBrightness(color, brightness, saturation, contrast);
  float v = vignette(uv, vignetteSize, vignetteRoundness, vignetteSmoothness);
  vec3 vig = color / v;
  vec3 g = gaussgrain(time) * 0.06;
  color = mix(color, vig, vignetteMix);
  color = mix(prgm2.rgb, color, amount) - g;
  color = clamp(color, 0.0, 1.0);
  gl_FragColor = vec4(color, 1.0);
}