{
    "name": "Low Res",
    "passes": [
      {
        "name": "lowRes",
        "inputs": [
          "default"
        ],
        "glsl": "
  varying vec2 sourceTextureCoordinate;
  uniform float blockSize;
  uniform float maxColors;
  uniform float aberration;
  uniform vec2 outputSize;

  void main() {

    vec2 uv = sourceTextureCoordinate;

    // ensure pixel size looks the same in all resolutions (1080, 2K)
    float bs = blockSize * outputSize.y / 1920.0;

    vec2 st = (uv - 0.5) * outputSize;
    vec2 pixelatedST = floor(st / bs) * bs + bs * 0.5;
    vec2 pixelatedUV = pixelatedST / outputSize + 0.5;

    vec4 baseR = sampleInput(pixelatedUV + aberration);
    vec4 baseG = sampleInput(pixelatedUV);
    vec4 baseB = sampleInput(pixelatedUV - aberration);
    vec4 base = vec4(baseR.r, baseG.g, baseB.b, baseR.a);

    // 8-bit color
    vec3 color_resolution = vec3(8.0, 8.0, 4.0) * maxColors / 256.0;
    vec3 color_bands = floor(base.rgb * color_resolution) / (color_resolution - 1.0);
    vec4 color = vec4(min(color_bands, 1.0), base.a);

    gl_FragColor = toOutputFormat(color);
  }
  ",
        "uniforms": {
          "blockSize": {
            "value": 5
          },
          "maxColors": {
            "value": 256
          },
          "aberration": {
            "value": 0.004
          }
        }
      }
    ]
  }
