PONG GAME

A tribute to the history of video games and to the pioneer giants whose shoulders we stand on.

Created by marcogomez on Fri, 29 Oct 2021 20:48:54 GMT.


#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

#define inputTexture prgm5Texture
uniform sampler2D prgm5Texture;
uniform vec2 resolution;

out vec4 fragColor;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec4 tex = texture(inputTexture, uv);
  fragColor = tex;
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

#define InOutTexture prgm1Texture
uniform sampler2D prgm1Texture;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform float deltatime;
uniform int frame;
uniform bool mousedown;

out vec4 fragColor;

const ivec2 texelPlayerPos = ivec2(0, 0);
const ivec2 texelGPUPos = ivec2(1, 0);
const ivec2 texelBallPosDir = ivec2(2, 0);
const ivec2 texelScore = ivec2(3, 0);
const ivec2 texelState = ivec2(4, 0);

const vec2 paddleSize = vec2(0.03, 0.16);
const float ballSize = 0.015;
const float wallWidth = 0.02;
const float fieldHeight = 1.05;

const float playerXPos = -0.95;
const float gpuXPos = 0.95;

const float ballSpeed = 1.5;
const float ballAccel = 0.2;
const float speedLimit = 3.0;

vec4 readTexel(sampler2D tex, ivec2 pos) {
  return texelFetch(tex, pos, 0);
}

void writeTexel(in ivec2 pos, in vec4 val, in ivec2 fragPos, inout vec4 fragColor) {
  fragColor = (fragPos == pos) ? val : fragColor;
}

float Hash(in float n) {
  return fract(sin(n) * 138.5453123);
}

float GetSpeed(float startTime) {
  return clamp(0.0, speedLimit, ballSpeed + (time - startTime) * ballAccel);
}

void main(void) {
  ivec2 iCurPixel = ivec2(gl_FragCoord.xy - 0.5);
  if (gl_FragCoord.x > 5.0 || gl_FragCoord.y > 1.0 ) discard;
  vec2 playerPaddlePos = readTexel(InOutTexture, texelPlayerPos).xy;
  vec2 gpuPaddlePos = readTexel(InOutTexture, texelGPUPos).xy;
  vec4 ballPosDir = readTexel(InOutTexture, texelBallPosDir);
  vec2 score = readTexel(InOutTexture, texelScore).xy;
  vec3 state = readTexel(InOutTexture, texelState).xyz;

  bool reset = (gpuPaddlePos == vec2(0.0));

  if (frame == 0 || state.x == 1.0 || reset) {
    ballPosDir = vec4(0.0, 0.0, 0.0, 0.0);
    playerPaddlePos = vec2(playerXPos, playerPaddlePos.y);
    gpuPaddlePos = vec2(gpuXPos, gpuPaddlePos.y);
    state.x = -1.0;
    state.z = 1.0;
    score = vec2(0, 0);
  }
  if (state.x == -1.0) {
    if (mousedown) {
      ballPosDir.xy = vec2(0.0, 0.0);
      ballPosDir.zw = normalize(vec2(state.z, Hash(float(frame) * 1.5) * 0.25));
      state.x = 0.0;
      state.y = time;
      gpuPaddlePos.y = 0.0;
      playerPaddlePos.y = 0.0;
    }
  } else {
    float limits = (fieldHeight * 0.5) - (wallWidth * 0.5);
    float paddleHalfHeight = paddleSize.y * 0.5;
    float paddleHalfWidth = paddleSize.x * 0.5;
    playerPaddlePos.y = clamp(mouse.y, -limits + paddleHalfHeight, limits - paddleHalfHeight);
    float gpuUp = step(0.0, ballPosDir.y - (gpuPaddlePos.y + paddleHalfHeight));
    float gpuDown = step(0.0, (gpuPaddlePos.y - paddleHalfHeight) - ballPosDir.y);
    gpuPaddlePos.y += (deltatime / 1000.0) * (gpuUp - gpuDown);
    gpuPaddlePos.y = clamp(gpuPaddlePos.y, -limits + paddleHalfHeight, limits - paddleHalfHeight);
    ballPosDir.xy += ballPosDir.zw * GetSpeed(state.y) * (deltatime / 1000.0);
    if (ballPosDir.y + ballSize >= limits) {
      ballPosDir.y = limits - ballSize;
      ballPosDir.w *= -1.0;
    } else if (ballPosDir.y - ballSize <= -limits) {
      ballPosDir.y = ballSize - limits;
      ballPosDir.w *= -1.0;
    }
    if (ballPosDir.x + ballSize >= (gpuPaddlePos.x - paddleHalfWidth)) {
      if (abs(ballPosDir.y - gpuPaddlePos.y) <= paddleHalfHeight + ballSize) {
        ballPosDir.x = gpuPaddlePos.x - paddleHalfWidth - ballSize;
        ballPosDir.z *= -1.0;
        ballPosDir.w += (ballPosDir.y - gpuPaddlePos.y) * 5.0;
        ballPosDir.zw = normalize(ballPosDir.zw);
      }
    } else if (ballPosDir.x - ballSize <= playerPaddlePos.x + paddleHalfWidth) {
      if(abs(ballPosDir.y - playerPaddlePos.y) <= paddleHalfHeight + ballSize) {
        ballPosDir.x = playerPaddlePos.x + paddleHalfWidth + ballSize;
        ballPosDir.z *= -1.0;
        ballPosDir.w += (ballPosDir.y - playerPaddlePos.y) * 5.0;
        ballPosDir.zw = normalize(ballPosDir.zw);
      }
    }
    if (ballPosDir.x - ballSize > gpuPaddlePos.x + paddleHalfWidth - 1e-4) {
      score.x += 1.0; state.x = -1.0; state.z = -1.0;
    } else if (ballPosDir.x + ballSize < playerPaddlePos.x - paddleHalfWidth + 1e-4) {
      score.y += 1.0; state.x = -1.0; state.z = 1.0;
    }
    if (score.x >= 10.0 || score.y >= 10.0) { state.x = 1.0; }
  }
  fragColor = vec4(0.0);
  writeTexel(texelPlayerPos, vec4(playerPaddlePos, 0.0, 0.0), iCurPixel, fragColor);
  writeTexel(texelGPUPos, vec4(gpuPaddlePos, 0.0, 0.0), iCurPixel, fragColor);
  writeTexel(texelBallPosDir, ballPosDir, iCurPixel, fragColor);
  writeTexel(texelScore, vec4(score, 0.0, 0.0), iCurPixel, fragColor);
  writeTexel(texelState, vec4(state, 0.0), iCurPixel, fragColor);
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

#define inputTexture prgm1Texture
uniform sampler2D prgm1Texture;
uniform vec2 resolution;
uniform float time;

out vec4 fragColor;

const ivec2 texelPlayerPos = ivec2(0, 0);
const ivec2 texelGPUPos = ivec2(1, 0);
const ivec2 texelBallPosDir = ivec2(2, 0);
const ivec2 texelScore = ivec2(3, 0);
const ivec2 texelState = ivec2(4, 0);

const float drawSS = 0.01;
const vec2 paddleSize = vec2(0.03, 0.16);
const float wallWidth = 0.01;
const float ballSize = 0.015;
const float fieldHeight = 1.05;

const vec3 ballColor = vec3(2.0);
const vec3 paddleColor = vec3(0.6, 0.7, 0.8);
const vec3 borderColor = vec3(0.7, 0.8, 0.7);
const vec3 scoreColor = vec3(0.3);

vec4 readTexel(sampler2D tex, ivec2 pos) {
  return texelFetch(tex, pos, 0);
}

float osc(float s, float e, float t, float ts) {
  return (e - s) / 2.0 + s + sin(t * ts) * (e - s) * 0.5;
}

const int[] font = int[](0x75557, 0x22222, 0x74717, 0x74747, 0x11574, 0x71747, 0x71757, 0x74444, 0x75757, 0x75747);
const int[] powers = int[](1, 10, 100, 1000, 10000);
float printInt(vec2 uv, int value ) {
  const int maxDigits = 2;
  if (abs(uv.y - 0.5) < 0.5) {
    int iu = int(floor(uv.x));
    if (iu >= 0 && iu < maxDigits) {
      int n = (value / powers[maxDigits - iu - 1]) % 10;
      uv.x = fract(uv.x);
      ivec2 p = ivec2(floor(uv*vec2(4.0, 5.0)));
      return float((font[n] >> (p.x + p.y * 4)) & 1);
    }
  }
  return 0.0;
}

float squareSDF(vec2 p, vec2 pos, vec2 size) {
  vec2 d = abs(p - pos) - size;
  return length(max(d, 0.0));
}

vec3 drawBall(vec2 pos, vec2 ballPos, vec3 col) {
  float t = squareSDF(pos, ballPos, vec2(ballSize, ballSize));
  return mix(ballColor, col, smoothstep(0.0, drawSS, t));
}

vec3 drawPaddle(vec2 pos, vec2 paddlePos, vec3 col) {
  float t = squareSDF(pos, paddlePos, paddleSize * 0.5);
  return mix(paddleColor, col, smoothstep(0.0, drawSS, t));
}

vec3 drawWalls(vec2 pos, float distToCenter, vec3 col, bool point) {
  float t = (point) ? 0.25 * time : 0.0625 * time;
  float p = abs(abs(pos.y) - distToCenter) / (wallWidth * 0.5);
  return mix(
    borderColor,
    col,
    smoothstep(
      0.0,
      drawSS,
      clamp(
        sin((sign(pos.y) * t + pos.x) * 64.0),
        (point) ? 0.0 : 0.0,
        (point) ? 0.5 : 0.0
      ) + p - 1.0
    )
  );
}

vec3 drawDashedLine(vec2 pos, float opacity, float thickness, vec3 col) {
  float t = abs(pos.x) - thickness;
  float dashT = step(0.0, sin(pos.y * 220.0 + 1.5));
  float limitsT = (abs(pos.y) - (fieldHeight * 0.5));
  col = mix(vec3(opacity), col, smoothstep(0.0, thickness, max(t, limitsT) + dashT));
  return col;
}

vec3 drawScore(vec2 score, vec2 coord, vec3 col, bool point, vec3 state) {
  float blinkPlayer = 1.0;
  float blinkGPU = 1.0;
  float o = osc(-0.25, 1.0, time, 8.0);
  if (point && (score.x > 0.0 || score.y > 0.0)) {
    if (state.z == -1.0) { blinkPlayer += o; }
    if (state.z == +1.0) { blinkGPU += o; }
  }
  const vec2 displacement = vec2(0.3, -0.65);
  vec2 uv = (2.0 * coord.xy - resolution.xy) / resolution.y;
  col = mix(col, scoreColor * blinkPlayer, printInt((uv + displacement) * vec2(10.0, 7.0), int(score.x)));
  col = mix(col, scoreColor * blinkGPU, printInt((uv + displacement * vec2(-0.4, 1.0)) * vec2(10.0, 7.0), int(score.y)));
  return col;
}

void main(void) {
  vec2 uv = ((gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0);
  uv = uv * vec2(1.15, 1.15 * (resolution.y / resolution.x));
  vec2 playerPos = readTexel(inputTexture, texelPlayerPos).xy;
  vec2 gpuPos = readTexel(inputTexture, texelGPUPos).xy;
  vec2 ballPos = readTexel(inputTexture, texelBallPosDir).xy;
  vec2 score = readTexel(inputTexture, texelScore).xy;
  vec3 state = readTexel(inputTexture, texelState).xyz;
  bool point = (state.x == -1.0);
  vec3 col = vec3(0.02);
  col = drawDashedLine(uv, 0.4, 0.003, col);
  col = drawDashedLine(vec2(uv.x + 1.0, uv.y), 0.3, 0.002, col);
  col = drawDashedLine(vec2(uv.x - 1.0, uv.y), 0.3, 0.002, col);
  col = drawBall(uv, ballPos, col);
  col = drawPaddle(uv, playerPos, col);
  col = drawPaddle(uv, gpuPos, col);
  col = drawWalls(uv, (fieldHeight * 0.5), col, point);
  col = drawScore(score, gl_FragCoord.xy, col, point, state);
  fragColor = vec4(col, 1.0);
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

#define inputTexture prgm2Texture
#define statesTexture prgm1Texture
uniform sampler2D prgm1Texture;
uniform sampler2D prgm2Texture;
uniform sampler2D prgm3Texture;
uniform sampler2D textTexture;
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform float time;
uniform int frame;
uniform float fftNormalized;

out vec4 fragColor;

const ivec2 texelScore = ivec2(3, 0);
const ivec2 texelState = ivec2(4, 0);

const float h = 0.003;
const float v = 0.012;
const float scrollSpeed = 3.0;
const float fontSize = 0.4;
const float sinAmplitude = 0.6;
const float sinFrequency = 0.4;
const float sinSpeed = 3.0;
const float scrollLength = 50.0;
const vec4 baseColor = vec4(0.0, 0.0, 1.0, 0.0);
#define SS(a) c += char(u, a); u.x -= 0.5;

vec4 char(vec2 pos, float c) {
  vec4 o = texture(
    textTexture,
    clamp(pos, 0.0, 1.0) / 16.0 + fract(floor(vec2(c, 15.999 - c / 16.0)) / 16.0)
  );
  return(
    (o.r > 0.0) ? baseColor * smoothstep(0.0, 0.5, o.r - o.a * 0.2) : vec4(0.0)
  );
}

#define _spc SS(32.);
#define _A SS(65.);
#define _B SS(66.);
#define _C SS(67.);
#define _D SS(68.);
#define _E SS(69.);
#define _F SS(70.);
#define _G SS(71.);
#define _H SS(72.);
#define _I SS(73.);
#define _J SS(74.);
#define _K SS(75.);
#define _L SS(76.);
#define _M SS(77.);
#define _N SS(78.);
#define _O SS(79.);
#define _P SS(80.);
#define _Q SS(81.);
#define _R SS(82.);
#define _S SS(83.);
#define _T SS(84.);
#define _U SS(85.);
#define _V SS(86.);
#define _W SS(87.);
#define _X SS(88.);
#define _Y SS(89.);
#define _Z SS(90.);
#define _a SS(97.);
#define _b SS(98.);
#define _c SS(99.);
#define _d SS(100.);
#define _e SS(101.);
#define _f SS(102.);
#define _g SS(103.);
#define _h SS(104.);
#define _i SS(105.);
#define _j SS(106.);
#define _k SS(107.);
#define _l SS(108.);
#define _m SS(109.);
#define _n SS(110.);
#define _o SS(111.);
#define _p SS(112.);
#define _q SS(113.);
#define _r SS(114.);
#define _s SS(115.);
#define _t SS(116.);
#define _u SS(117.);
#define _v SS(118.);
#define _w SS(119.);
#define _x SS(120.);
#define _y SS(121.);
#define _z SS(122.);
#define _or SS(33.);
#define _dot SS(46.);

vec4 scroll(vec2 u) {
  vec4 c = vec4(0.0);
   _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc;
  _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc _spc;
  _C _L _I _C _K _spc _T _O _spc _P _L _A _Y;
  return c;
}

vec4 readTexel(sampler2D tex, ivec2 pos) {
  return texelFetch(tex, pos, 0);
}

float stepm(float a, float b, float c) {
  return step(c, sin(time + a * cos(time * b)));
}

vec3 badVHS(sampler2D tex, vec2 uv, vec3 state, vec2 score) {
  vec2 suv = gl_FragCoord.xy / resolution.xy;
  float tmod = mod(time * 0.25, 3.0);
  float lookyMod = uv.y - tmod;
  float window = 1.0 / (1.0 + 20.0 * lookyMod * lookyMod);
  float lookyStep = stepm(4.0, 4.0, 0.3) * 0.5;
  uv.x = uv.x + sin(uv.y * 10.0 + time) / 100.0 * lookyStep * (1.0 + cos(time * 80.0)) * window * 0.25;
  float vShift = v * stepm(2.0, 3.0, 0.9) * (sin(time) * sin(time * 20.0) + (0.5 + 0.1 * sin(time * 200.0) * cos(time)));
  uv.y = mod(uv.y + vShift, 5.0);
  vec3 desatColor;
  float r, g, b;
  float x = sin(0.3 * time + uv.y * 21.0) * sin(0.7 * time + uv.y * 29.0) * sin(0.3 + 0.33 * time + uv.y * 31.0) * h;
  r = texture(tex, vec2(x + uv.x + 0.001, uv.y + 0.001)).x + 0.007;
  g = texture(tex, vec2(x + uv.x + 0.000, uv.y - 0.002)).y + 0.007;
  b = texture(tex, vec2(x + uv.x - 0.002, uv.y + 0.000)).z + 0.007;
  r += 0.08 * texture(tex, 0.75 * vec2(x +  0.012, -0.013) + vec2(uv.x + 0.001, uv.y + 0.001)).x;
  g += 0.05 * texture(tex, 0.75 * vec2(x + -0.011, -0.010) + vec2(uv.x + 0.000, uv.y - 0.002)).y;
  b += 0.08 * texture(tex, 0.75 * vec2(x + -0.010, -0.009) + vec2(uv.x - 0.002, uv.y + 0.000)).z;
  float luma = 0.3 * r + 0.6 * g + 0.1 * b;
  float desat = 0.3;
  desatColor = vec3(
    r + desat * (luma - r),
    g + desat * (luma - g),
    b + desat * (luma - b)
  );
  if (state.x == -1.0 && (score.x < 1.0 && score.y < 1.0)) {
    desatColor *= (1.0 + state.x * 0.9) * clamp((suv.y - 0.35), 0.0, 1.0);
    float yMod = 64.0;
    float yRes = resolution.y / yMod;
    vec2 suv = (gl_FragCoord.xy / resolution.y * 2.0 - 1.0) / fontSize;
    suv.x += -4.0 + mod(time * scrollSpeed, scrollLength);
    suv.y += floor((0.5 + sinAmplitude * sin(suv.x * sinFrequency + time * sinSpeed)) * yMod * 0.42) / (yMod * 0.42);
    suv = floor(suv * yRes) / yRes;
    desatColor = (scroll(vec2(suv.x, suv.y + 1.2)) == baseColor)
      ? vec3(0.7, 0.8, 0.7)
      : desatColor;
  }
  return desatColor;
}

vec3 hash33(vec3 p3) {
  p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973));
  p3 += dot(p3, p3.yxz + 19.19);
  return fract((p3.xxy + p3.yxx) * p3.zyx);
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec3 state = readTexel(statesTexture, texelState).xyz;
  vec2 score = readTexel(statesTexture, texelScore).xy;
  vec4 tex = texture(inputTexture, uv);
  vec3 vhs = badVHS(inputTexture, uv, state, score);
  float vhsGlitches = state.x + 1.0;
  vec4 col = mix(vec4(vhs, 1.0), tex, vhsGlitches);
  float w = 0.1;
  vec2 a = vec2(uv.x - 0.5, uv.y - 0.66);
  vec2 b = a * 0.15 / float(10.0);
  vec3 h = hash33(vec3(gl_FragCoord.xy, frame));
  uv += b * h.x;
  for (float i = 1.0; i > 0.994; i-= 0.0005) {
    uv -= 0.5; uv *= i; uv += 0.5;
    col += texture(inputTexture, uv) * w * 1.5;
    w *= 0.98;
  }
  col *= 0.98;
  if (frame % 2 == 0) { fragColor += col; }
  fragColor += texture(prgm3Texture, uv) * 0.85;
  fragColor = mix(fragColor, col, 0.65);
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

#define inputTexture prgm2Texture
uniform sampler2D prgm1Texture;
uniform sampler2D prgm2Texture;
uniform sampler2D prgm3Texture;
uniform sampler2D noiseTexture;
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform float time;

out vec4 fragColor;

const float amount = 1.0;
const float reinhardAmount = 0.0;
const float contrast = 1.0;
const float brightness = 1.0;
const float saturation = 1.0;
const vec2 vignetteSize = vec2(0.25, 0.25);
const float vignetteRoundness = 0.15;
const float vignetteMix = 1.0;
const float vignetteSmoothness = 0.6;
const float gaussGrainAmount = 0.3;
const float W = 1.2;
const float T = 7.5;

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float sqrTAU = sqrt(TAU);

vec4 readTexel(sampler2D tex, ivec2 pos) {
  return texelFetch(tex, pos, 0);
}

vec4 rgbShift(vec2 p , vec4 shift) {
  shift *= 2.0 * shift.w - 1.0;
  vec2 rs = vec2(shift.x, -shift.y);
  vec2 gs = vec2(shift.y, -shift.z);
  vec2 bs = vec2(shift.z, -shift.x);
  float r = texture(inputTexture, p + rs, 0.0).x;
  float g = texture(inputTexture, p + gs, 0.0).y;
  float b = texture(inputTexture, p + bs, 0.0).z;
  return vec4(r,g,b,1.0);
}

vec4 noise(vec2 uv) {
  return texture(noiseTexture, uv, 0.0);
}

vec4 vec4pow(vec4 v, float p) {
  return vec4(pow(v.x, p), pow(v.y, p), pow(v.z, p), v.w);
}

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 * sqrTAU)) *
    (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 drawScanLines(vec2 uv, inout vec3 color) {
  float scanline = clamp(0.95 + 0.05 * cos(PI * (uv.y + 0.016 * -time * 1.5) * 160.0 * 1.0 ), 0.0, 1.0);
  float grille = 0.85 + 0.15 * clamp(1.5 * cos(PI * uv.x * 320.0 * 1.0), 0.0, 1.0);
  color -= scanline * grille * 0.03;
}

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 mo = uv * 2.0 - 1.0;
  mo *= 0.027;
  float frameScale = 29.97;
  float frameTime = floor(time * frameScale) / frameScale;
  vec3 grain = gaussgrain(frameTime);
  vec3 chromaticAberration;
  chromaticAberration.r = texture(inputTexture, uv - mo * 0.05, 0.0).r;
  chromaticAberration.g = texture(inputTexture, uv - mo * 0.15, 0.0).g;
  chromaticAberration.b = texture(inputTexture, uv - mo * 0.25, 0.0).b;
  vec4 tex = vec4(vec3(0.0), 1.0);
  tex.xyz = mix(tex.xyz, chromaticAberration, 0.3);
  const float speed = 0.007;
  const float amplitude = 0.017;
  vec4 shift = vec4pow(
    noise(
      vec2(speed * time, speed * time / 25.0)
    ), 8.0
  ) * vec4(vec3(amplitude), 1.0);
  tex += rgbShift(uv, shift);
  vec3 reinhard = filmicReinhard(tex.rgb);
  vec3 color = tex.rgb;
  color = mix(color, reinhard, reinhardAmount);
  color = contrastSaturationBrightness(color, brightness, saturation, contrast);
  float v = vignette(uv, vignetteSize, vignetteRoundness, vignetteSmoothness);
  vec3 vig = color * v;
  color = mix(color, vig, vignetteMix);
  color = mix(tex.xyz, color, amount);
  color -= grain * gaussGrainAmount;
  color = clamp(color, 0.0, 1.0);
  vec4 finalColor = vec4(color, 1.0);
  vec4 prgm3 = texture(prgm3Texture, uv);
  finalColor = mix(finalColor, prgm3, 0.5);
  finalColor.rb *= 0.9;
  drawScanLines(uv, finalColor.xyz);
  fragColor = finalColor;
}

#version 300 es
// ╔═════════════╦════════════════╗
// ║ Marco Gomez ║ https://mgz.me ║
// ╚═════════════╩════════════════╝
precision highp float;

#define inputTexture prgm4Texture
uniform sampler2D prgm4Texture;
uniform vec2 resolution;
uniform vec2 mouselerp;
uniform float time;

out vec4 fragColor;

#define ss smoothstep

const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const vec2 hashv2 = vec2(12.9898, 78.233);
const float hashS = 43758.5453123;
const int blurIter = 8;
const float blurSize = 0.07;
const float width = 0.49;
const float height = 0.3;

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, hashv2);
  float noise = fract(sin(seed) * hashS + t);
  noise = gaussian(noise, 0.0, 0.5);
  return vec3(noise);
}

float rand(vec2 co) {
  return fract(sin(dot(co.xy, hashv2)) * hashS);
}

vec2 CurvedSurface(vec2 uv, float r) {
  return r * uv / sqrt(r * r - dot(uv, uv));
}

vec2 crtCurve(vec2 uv, float r, bool content) {
  uv = CurvedSurface(uv, 1.8);
  if (content) { uv *= 0.5 / vec2(width, height); }
  uv.x -= mouselerp.x * 0.0325;
  return uv;
}

float roundSquare(vec2 p, vec2 b, float r) {
  return length(max(abs(p) - b, 0.0)) - r;
}

float rs(vec2 uv, float r) {
  return roundSquare(uv, vec2(width, height) + r, 0.05);
}

vec2 borderReflect(vec2 p, float r) {
  float eps = 0.0001;
  vec2 epsx = vec2(eps, 0.0);
  vec2 epsy = vec2(0.0, eps);
  vec2 b = (1.0 + vec2(r, r)) * 0.5;
  r /= 3.0;
  p -= 0.5;
  vec2 normal = vec2(
    roundSquare(p - epsx, b, r) - roundSquare(p + epsx, b, r),
    roundSquare(p - epsy, b, r) - roundSquare(p + epsy, b, r)
  ) / eps;
  float d = roundSquare(p, b, r);
  p += 0.5;
  return p + d * normal;
}

vec2 normalizeAndFix() {
  vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0;
  float targetAR = 16.0 / 9.0;
  float ar = resolution.x / resolution.y;
  uv.x *= ar;
  uv *= (
    ar < targetAR &&
    resolution.x < 800.0 &&
    resolution.x < resolution.y
  ) ? 1.1 : 0.55;
  return uv;
}

void main(void) {
  float s = 0.0021;
  vec4 color = vec4(0.02, 0.02, 0.03, 0.0);
  const vec4 multColor = vec4(0.21);
  const float ambient = 0.11;
  vec4 bezel = vec4(0.5);
  vec2 uv = normalizeAndFix();
  vec2 suv = gl_FragCoord.xy / resolution.xy;
  float gs = 1.0;
  float grid = (
    (mod(floor((suv.x) * resolution.x / gs), 2.0) == 0.0 ? 1.0 : 0.0) *
    (mod(floor((suv.y) * resolution.y / gs), 2.0) == 0.0 ? 1.0 : 0.0)
  );
  vec2 uvC = crtCurve(uv, 1., true) + 0.5;
  vec2 uvS = crtCurve(uv, 1., false);
  vec2 uvE = crtCurve(uv, 1.25, false);
  color += (max(0.0, ambient - 0.25 * distance(uvS, vec2(0.0))) * ss(s, -s, rs(uvS, 0.0)));
  color += (bezel * ambient * 0.7 * ss(-s, s, rs(uvS, 0.0)) * ss(s, -s, rs(uvE, 0.05)));
  color -= (bezel * ss(-s * 2.0, s * 10.0, rs(uvE, 0.05)) * ss(s * 2.0, -s * 2.0, rs(uvE, 0.05)));
  color += (bezel * ambient * ss(-s, s, rs(uvE, 0.05)) * ss(s, -s, rs(uvE, 0.15)));
  for (int i = 0; i < blurIter; i++) {
    vec2 uvR = borderReflect(uvC + (vec2(rand(uvC + float(i)), rand(uvC + float(i) + 0.1)) - 0.5) * blurSize, 0.05);
    color += (
      (multColor - bezel * ambient) *
      texture(inputTexture, uvR) / float(blurIter) *
      ss(-s, s, rs(uvS, 0.0)) *
      ss(s, -s, rs(uvE, 0.05))
    );
  }
  vec4 prgmMipMaps = texture(inputTexture, uvC, 3.0) + texture(inputTexture, uvC, 4.0) + texture(inputTexture, uvC, 5.0);
  color += (prgmMipMaps * ss(0.0, -s * 20.0, rs(uvS, -0.05)) * 0.5);
  color += (
    max(0.0, (1.0 - 2.0 * gl_FragCoord.y / resolution.y)) * vec4(0.1, 0.1, 0.1, 0.0) *
    ss(-0.3, 0.3, roundSquare(uvC - vec2(0.5, 0.0), vec2(width + 0.2, height), 0.1)) *
    ss(-s * 2.0, s * 2.0, roundSquare(uvE, vec2(width, height) + 0.15, 0.05))
  ) * 1.5;
  if (
    uvC.x > 0.0 &&
    uvC.x < 1.0 &&
    uvC.y > 0.0 &&
    uvC.y < 1.0
  ) {
    color += texture(inputTexture, uvC);
    color = mix(color, color * grid, 0.35);
  }
  vec3 g = gaussgrain(time) * 0.015;
  color.xyz += g;
  fragColor = color;
  fragColor.a = 1.0;
}