Trippy Gameboy DotsCam

A trippy cam visualization combining edge detection, dithering, movement detection, and a Gameboy color palette.

Created by marcogomez on Sun, 26 Sep 2021 02:25:49 GMT.


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

uniform sampler2D prgm5Texture;
uniform vec2 resolution;

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

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

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

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec3 old = texture2D(prgm1Texture, uv).xyz * 0.96;
  vec3 new = (
    cos(3.0 + uv.xyx + vec3(0.0, 2.0, 4.0)) *
    length(texture2D(prgm3Texture, uv).xyz - texture2D(prgm2Texture, uv).xyz)
  );
  gl_FragColor = vec4(clamp(old + new * (1.0 - old), 0.0, 1.0), 1.0);
}

// ╔═════════════╦═════════════════╦════════════════╗
// ║ 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;
  gl_FragColor = vec4(texture2D(prgm3Texture, uv).xyz, 1.0);
}

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

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

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float bgWaveSpeed = 2.0;
const float xDistMag = 0.05;
const float yDistMag = 0.05;
const float xSineCycles = TAU;
const float ySineCycles = TAU;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 suv = uv;
  float ar = resolution.x / resolution.y;
  uv = uv * 2.0 - 1.0; uv *= 0.9 - (fft * 0.2);
  float minRes = min(resolution.x, resolution.y);
  vec2 fc = gl_FragCoord.xy / minRes;
  float wt = time * bgWaveSpeed;
  float xAngle = wt + fc.y * ySineCycles;
  float yAngle = wt + fc.x * xSineCycles;
  bool bxHalf, byHalf;
  vec2 distortOffset = vec2(sin(xAngle), sin(yAngle)) * vec2(xDistMag, yDistMag) * 0.3;
  suv += distortOffset * 0.75;
  uv += distortOffset;
  uv = uv * 0.5 + 0.5;
  vec2 uvar = uv * vec2(ar, 16.0 / 9.0) * 0.5;
  vec4 finalColor = vec4(texture2D(camTexture, uvar).xyz, 1.0);
  gl_FragColor = mix(finalColor, finalColor + texture2D(prgm1Texture, suv) * 1.5, 0.75);
}

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

uniform sampler2D prgm1Texture;
uniform sampler2D prgm3Texture;
uniform vec2 resolution;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 prgm1 = texture2D(prgm1Texture, uv);
  vec4 prgm3 = texture2D(prgm3Texture, uv);
  float gray = length(prgm3.xyz);
  vec4 edge = vec4(vec3(step(0.06, length(vec2(dFdx(gray), dFdy(gray))))), 1.0);
  edge = mix(edge, prgm3, 0.25);
  gl_FragColor = mix(prgm1, edge, 0.17);
}

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

uniform sampler2D prgm4Texture;
uniform vec2 resolution;

const float reinhardAmount = 0.6;
const float contrast = 1.3;
const float brightness = 1.3;
const float saturation = 0.8;
const vec2 vignetteSize = vec2(0.25, 0.25);
const float vignetteRoundness = 0.3;
const float vignetteMix = 0.5;
const float vignetteSmoothness = 0.5;
const float W = 1.2;
const float T = 7.5;

vec3 findClosest(in vec3 ref) {
  vec3 old = vec3 (100.0 * 255.0);
  #define try(new) old = mix (new, old, step (length (old-ref), length (new-ref)));
  try(vec3(048.0, 048.0, 048.0)); // L20
  try(vec3(000.0, 000.0, 000.0));
  try(vec3(041.0, 057.0, 065.0));
  try(vec3(072.0, 093.0, 072.0));
  try(vec3(133.0, 149.0, 080.0));
  try(vec3(186.0, 195.0, 117.0));
  try(vec3(242.0, 239.0, 231.0));
  return old;
}

const mat4 bayertl = mat4(
  00.0 / 64.0, 32.0 / 64.0, 08.0 / 64.0, 40.0 / 64.0,
  48.0 / 64.0, 16.0 / 64.0, 56.0 / 64.0, 24.0 / 64.0,
  12.0 / 64.0, 44.0 / 64.0, 04.0 / 64.0, 36.0 / 64.0,
  60.0 / 64.0, 28.0 / 64.0, 52.0 / 64.0, 20.0 / 64.0
);

const mat4 bayertr = mat4(
  02.0 / 64.0, 34.0 / 64.0, 10.0 / 64.0, 42.0 / 64.0,
  50.0 / 64.0, 18.0 / 64.0, 58.0 / 64.0, 26.0 / 64.0,
  14.0 / 64.0, 46.0 / 64.0, 06.0 / 64.0, 38.0 / 64.0,
  62.0 / 64.0, 30.0 / 64.0, 54.0 / 64.0, 22.0 / 64.0
);

const mat4 bayerbl = mat4(
  03.0 / 64.0, 35.0 / 64.0, 11.0 / 64.0, 43.0 / 64.0,
  51.0 / 64.0, 19.0 / 64.0, 59.0 / 64.0, 27.0 / 64.0,
  15.0 / 64.0, 47.0 / 64.0, 07.0 / 64.0, 39.0 / 64.0,
  63.0 / 64.0, 31.0 / 64.0, 55.0 / 64.0, 23.0 / 64.0
);

const mat4 bayerbr = mat4(
  01.0 / 64.0, 33.0 / 64.0, 09.0 / 64.0, 41.0 / 64.0,
  49.0 / 64.0, 17.0 / 64.0, 57.0 / 64.0, 25.0 / 64.0,
  13.0 / 64.0, 45.0 / 64.0, 05.0 / 64.0, 37.0 / 64.0,
  61.0 / 64.0, 29.0 / 64.0, 53.0 / 64.0, 21.0 / 64.0
);

float dither(mat4 m, ivec2 p) {
  if (p.y == 0) {
    if (p.x == 0) { return m[0][0]; }
    else if (p.x == 1) { return m[1][0]; }
    else if (p.x == 2) { return m[2][0]; }
    else { return m[3][0]; }
  } else if (p.y == 1) {
    if (p.x == 0) { return m[0][1]; }
    else if (p.x == 1) { return m[1][1]; }
    else if (p.x == 2) { return m[2][1]; }
    else { return m[3][1]; }
  } else if (p.y == 2) {
    if (p.x == 0) { return m[0][1]; }
    else if (p.x == 1) { return m[1][2]; }
    else if (p.x == 2) { return m[2][2]; }
    else { return m[3][2]; }
  } else {
    if (p.x == 0) { return m[0][3]; }
    else if (p.x == 1) { return m[1][3]; }
    else if (p.x == 2) { return m[2][3]; }
    else { return m[3][3]; }
  }
}

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);
}

void main(void) {
  ivec2 p = ivec2(mod(gl_FragCoord.xy, 8.0));
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 res = vec2(320.0, 200.0);
  uv = vec2(floor(uv.x * res.x + 0.5) / res.x, floor(uv.y * res.y + 0.5) / res.y);
  vec4 prgm4 = texture2D(prgm4Texture, uv) * 1.5;
  vec3 c = prgm4.xyz;
  vec3 reinhard = filmicReinhard(c);
  c = mix(c + vec3(0.1), reinhard, reinhardAmount);
  c = ContrastSaturationBrightness(c, brightness, saturation, contrast);
  c = mix(c, findClosest(c * 255.0) / 250.0, 0.5);
  float v = vignette(uv, vignetteSize, vignetteRoundness, vignetteSmoothness);
  vec3 vig = c * v;
  c = mix(c, vig, vignetteMix);
  c = pow(abs(c), vec3(2.2));
  c -= 1.0 / 255.0;
  vec3 d = vec3(0.0);
  if (p.x <= 3 && p.y <= 3) {
    d.r = float(c.r > dither(bayertl, p));
    d.g = float(c.g > dither(bayertl, p));
    d.b = float(c.b > dither(bayertl, p));
  } else if (p.x > 3 && p.y <= 3) {
    d.r = float(c.r > dither(bayertr, p - ivec2(4, 0)));
    d.g = float(c.g > dither(bayertr, p - ivec2(4, 0)));
    d.b = float(c.b > dither(bayertr, p - ivec2(4, 0)));
  } else if (p.x <= 3 && p.y > 3) {
    d.r = float(c.r > dither(bayerbl, p - ivec2(0, 4)));
    d.g = float(c.g > dither(bayerbl, p - ivec2(0, 4)));
    d.b = float(c.b > dither(bayerbl, p - ivec2(0, 4)));
  } else if (p.x > 3 && p.y > 3) {
    d.r = float(c.r > dither(bayerbr, p - ivec2(4, 4)));
    d.g = float(c.g > dither(bayerbr, p - ivec2(4, 4)));
    d.b = float(c.b > dither(bayerbr, p - ivec2(4, 4)));
  }
  d = findClosest(clamp(d * 255.0, 0.0, 255.0)) / 255.0;
  vec4 color = vec4(d, 1.0);
  gl_FragColor = color;
}