The Code Therapy

PS3 Menu

A Shader driven by nostalgia to draw a simplistic reminder of the PS3 Menu Background.

Created by marcogomez on Fri, 05 Jul 2024 19:14:37 GMT.


#version 300 es

// Marco Gomez - @TheCodeTherapy - https://mgz.me
// Inspired by: PS3 XMB https://www.youtube.com/watch?v=ZuUmT4XL-bQ

precision highp float;

in vec2 vUv;
uniform vec2 resolution;
uniform float time;
uniform float fft;
out vec4 fragColor;

const vec3 colA = vec3(0.1, 0.3, 0.6);
const vec3 colB = vec3(0.0, 0.1, 0.5);
const vec3 colWave = vec3(0.35, 0.36, 0.37);
const float widthFactor = 1.5;
const float PI = acos(-1.0);
const float TAU = PI * 2.0;
const float PHI = (1.0 + sqrt(5.0)) / 2.0;
const float TAN30 = 1.0 / sqrt(3.0);
const vec2 hashv2 = vec2(12.9898, 78.233);
const float hashS = 43758.5453123;

#define ss(a, b, t) smoothstep(a, b, t)
#define clamps(a) clamp(a, 0.0, 1.0)

vec2 distortUV(vec2 uv, float freqX, float freqY, float distMult, float speed) {
  const float xDistMag = 0.02;
  const float yDistMag = 0.12;
  float minRes = min(resolution.x, resolution.y);
  vec2 fc = gl_FragCoord.xy / minRes;
  float wt = time * speed;
  float xAngle = wt + fc.y * freqY;
  float yAngle = wt + fc.x * freqX;
  bool bxHalf, byHalf;
  vec2 distortOffset = vec2(sin(xAngle), sin(yAngle)) * vec2(xDistMag, yDistMag) * 0.5;
  uv += distortOffset * distMult;
  return uv;
}

vec3 waveDist(float speed, float freq, float ampl, float offset, float width, bool up) {
  vec2 uv = distortUV(vUv, TAU * 0.4, TAU, 0.6, 0.5);
  float t = time + 300.0;
  float angle = t * speed * freq * -1.0 + uv.x * 2.0;
  float y = sin(angle) * ampl + offset;
  float diff = y - uv.y;
  bool positive = y - uv.y > 0.0 && uv.y != 0.0;
  float dist = distance(y, uv.y);
  float scale = 1.0;
  dist = up && positive || !up && !positive ? dist * 3.0 : dist;
  scale = pow(ss(width, 0.0, dist), 5.0);
  return min(colWave * scale, colWave);
}

float lineDist(vec2 p, vec2 a, vec2 b) {
  vec2 pa = p - a;
  vec2 ba = b - a;
  float t = clamps(dot(pa, ba) / dot(ba, ba));
  return length(pa - ba * t);
}

float hash(vec2 uv) {
  return fract(sin(dot(uv.xy, vec2(12.9898, 78.233))) * 43758.5453);
}

vec2 hash2(vec2 uv) {
  return vec2(hash(uv), hash(uv) * PHI);
}

vec2 getRandomPos(vec2 gID, float posTime) {
  vec2 rnd = hash2(gID) * posTime + fft * 2.0;
  return sin(rnd) * 0.35;
}

float getDots(vec2 uv, float uvMult, float posTime) {
  uv *= uvMult;
  vec2 gUV = fract(uv) - 0.5;
  vec2 gID = floor(uv);
  vec2 rndP = getRandomPos(gID, posTime);
  float rndDot = length(gUV - rndP);
  return ss(0.17, 0.01, rndDot);
}

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

vec3 hue(vec3 col, float a) {
  const vec3 k = vec3(TAN30);
  float c = cos(a);
  return col * c + cross(k, col) * sin(a) + k * dot(k, col) * (1.0 - c);
}

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

void main(void) {
  vec3 color = vec3(mix(colA, colB, vUv.x * vUv.y));
  float inten = 0.65 + fft * 0.2;
  float amplScale = 0.6;
  float speedMult = 0.6;
  color += waveDist(0.15 * speedMult, 0.26, 0.07 * amplScale, 0.35, 0.10, true) * inten;
  color += waveDist(0.21 * speedMult, 0.20, 0.20 * amplScale, 0.52, 0.10, false) * inten;
  color += waveDist(0.22 * speedMult, 0.58, 0.05 * amplScale, 0.30, 0.20, true) * inten;
  color += waveDist(0.30 * speedMult, 0.36, 0.07 * amplScale, 0.33, 0.10, true) * inten;
  color += waveDist(0.32 * speedMult, 0.60, 0.15 * amplScale, 0.45, 0.05, false) * inten;
  color += waveDist(0.44 * speedMult, 0.40, 0.15 * amplScale, 0.51, 0.10, false) * inten;
  color += waveDist(0.51 * speedMult, 0.46, 0.07 * amplScale, 0.30, 0.05, true) * inten;

  vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
  float dots = 0.03;
  for (float i = 1.0; i < 5.0; i += 1.0) {
    dots += dots * getDots(uv + i * 2.0, 27.0 + i * 2.0, (time + 100.0) * (0.5 + i / 12.0)) * (1.8 + fft * 4.0);
  }

  uv = distortUV(uv, TAU * 0.7, TAU, 1.0, 0.2);
  float particlesStripe = 1.0 - length(uv.y * 2.0);

  dots *= dots * dots * 50.0 * particlesStripe;
  color += pow(clamps(dots * (1.0 - length(uv.y * 3.0))), 1.2);
  float fade = clamps(time * 0.25 - 0.125);
  color = hue(color, osc(-0.1, -0.03, time));
  color = mix(color, color * color, osc(0.3, 0.6, time * 0.3)) - gaussgrain(time * 0.21) * 0.04;
  fragColor = vec4(color * fade, 1.0);
}