precision mediump float; uniform sampler2D u_texture; uniform vec2 u_resolution; uniform float u_time; uniform float u_screenShake; uniform float u_damageFlash; uniform float u_brightness; varying vec2 v_texCoord; // CRT curvature amount const float CURVATURE = 0.03; // Scanline intensity const float SCANLINE_INTENSITY = 0.15; const float SCANLINE_COUNT = 180.0; // Vignette const float VIGNETTE_INTENSITY = 0.3; // Chromatic aberration const float CHROMA_AMOUNT = 0.002; // Bloom threshold and intensity const float BLOOM_THRESHOLD = 0.7; const float BLOOM_INTENSITY = 0.15; // Apply barrel distortion for CRT curvature vec2 curveUV(vec2 uv) { uv = uv * 2.0 - 1.0; vec2 offset = abs(uv.yx) / vec2(6.0, 4.0); uv = uv + uv * offset * offset * CURVATURE; uv = uv * 0.5 + 0.5; return uv; } // Scanlines float scanline(vec2 uv) { float line = sin(uv.y * SCANLINE_COUNT * 3.14159); return 1.0 - SCANLINE_INTENSITY * (0.5 + 0.5 * line); } // Vignette effect float vignette(vec2 uv) { uv = uv * 2.0 - 1.0; float dist = length(uv * vec2(0.8, 1.0)); return 1.0 - smoothstep(0.7, 1.4, dist) * VIGNETTE_INTENSITY; } // Simple bloom by sampling bright areas vec3 bloom(sampler2D tex, vec2 uv, vec2 resolution) { vec3 sum = vec3(0.0); float pixelSize = 1.0 / resolution.x; // Sample in a cross pattern for (float i = -2.0; i <= 2.0; i += 1.0) { for (float j = -2.0; j <= 2.0; j += 1.0) { vec2 offset = vec2(i, j) * pixelSize * 2.0; vec3 sample = texture2D(tex, uv + offset).rgb; // Only add bright pixels float brightness = max(max(sample.r, sample.g), sample.b); if (brightness > BLOOM_THRESHOLD) { sum += sample * (brightness - BLOOM_THRESHOLD); } } } return sum * BLOOM_INTENSITY / 25.0; } // Chromatic aberration vec3 chromaticAberration(sampler2D tex, vec2 uv) { vec2 dir = uv - 0.5; float dist = length(dir); vec2 offset = dir * dist * CHROMA_AMOUNT; float r = texture2D(tex, uv + offset).r; float g = texture2D(tex, uv).g; float b = texture2D(tex, uv - offset).b; return vec3(r, g, b); } // Dithering pattern float dither(vec2 position) { int x = int(mod(position.x, 4.0)); int y = int(mod(position.y, 4.0)); int index = x + y * 4; // Bayer 4x4 dither matrix float threshold; if (index == 0) threshold = 0.0; else if (index == 1) threshold = 8.0; else if (index == 2) threshold = 2.0; else if (index == 3) threshold = 10.0; else if (index == 4) threshold = 12.0; else if (index == 5) threshold = 4.0; else if (index == 6) threshold = 14.0; else if (index == 7) threshold = 6.0; else if (index == 8) threshold = 3.0; else if (index == 9) threshold = 11.0; else if (index == 10) threshold = 1.0; else if (index == 11) threshold = 9.0; else if (index == 12) threshold = 15.0; else if (index == 13) threshold = 7.0; else if (index == 14) threshold = 13.0; else threshold = 5.0; return threshold / 16.0; } // Quantize color to limited palette vec3 quantize(vec3 color, float levels) { return floor(color * levels + 0.5) / levels; } void main() { // Apply screen shake vec2 uv = v_texCoord; if (u_screenShake > 0.0) { float shake = u_screenShake * 0.01; uv.x += sin(u_time * 50.0) * shake; uv.y += cos(u_time * 43.0) * shake; } // Apply CRT curvature vec2 curvedUV = curveUV(uv); // Check if we're outside the curved screen if (curvedUV.x < 0.0 || curvedUV.x > 1.0 || curvedUV.y < 0.0 || curvedUV.y > 1.0) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); return; } // Sample with chromatic aberration vec3 color = chromaticAberration(u_texture, curvedUV); // Add bloom color += bloom(u_texture, curvedUV, u_resolution); // Apply scanlines color *= scanline(curvedUV); // Apply vignette color *= vignette(curvedUV); // Subtle dithering for retro feel vec2 pixelPos = curvedUV * u_resolution; float ditherValue = dither(pixelPos); color += (ditherValue - 0.5) * 0.03; // Quantize to limited color palette (subtle) color = mix(color, quantize(color, 32.0), 0.2); // Apply brightness adjustment color *= u_brightness; // Apply damage flash (red tint) if (u_damageFlash > 0.0) { color = mix(color, vec3(1.0, 0.2, 0.1), u_damageFlash * 0.4); } // Ensure we don't exceed valid range color = clamp(color, 0.0, 1.0); gl_FragColor = vec4(color, 1.0); }