#version 450
// Post-process resolve: mixes the rendered scene toward luminance grayscale by uMono.
// The offscreen color target is stored top-left origin on Metal/Vulkan (vs GL's
// bottom-left), so sample with a flipped V to keep the resolved image upright.
layout(location = 0) in vec2 vUV;

layout(std140, binding = 0) uniform Globals {
    float uMono;
    float uTonemap;
    float uTrip;
    float uPcp;
    float uDungeonExposure;
    float uTime;
    vec2 uResolution;
};
layout(binding = 1) uniform sampler2D uTex;

layout(location = 0) out vec4 FragColor;

vec3 aces(vec3 x) {
    return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
}
void main() {
    vec2 uv = vec2(vUV.x, 1.0 - vUV.y);
    vec2 screenUV = uv;
    vec3 c;
    if (uTrip > 0.001) {
        vec2 p=uv-0.5;float radius=length(p);float angle=atan(p.y,p.x);float cornerMask=smoothstep(0.075,0.205,abs(p.x*p.y));
        if(uTrip>0.52){float fold=smoothstep(0.52,0.92,uTrip);float sector=6.28318/mix(8.0,14.0,fold);float mirrored=abs(mod(angle+sector*0.5,sector)-sector*0.5);angle=mix(angle,mirrored+sin(uTime*0.23)*sector,fold*0.72*cornerMask);}
        angle+=uTrip*(0.13*sin(uTime*0.75+radius*18.0)+0.055*sin(uTime*1.7-radius*31.0));
        radius*=1.0+uTrip*(0.055*sin(angle*7.0+uTime*1.15)+0.028*sin(uTime*0.42));uv=0.5+vec2(cos(angle),sin(angle))*radius;
        uv+=uTrip*vec2(sin(uv.y*27.0+uTime*1.8),cos(uv.x*23.0-uTime*1.4))*0.010;uv=clamp(uv,vec2(0.002),vec2(0.998));
        vec2 ca=normalize(p+vec2(0.0001))*(0.002+0.006*uTrip);
        c=vec3(texture(uTex,clamp(uv+ca,0.0,1.0)).r,texture(uTex,uv).g,texture(uTex,clamp(uv-ca,0.0,1.0)).b);
        float pulse=0.5+0.5*sin(uTime*1.35+radius*24.0);vec3 rainbow=0.5+0.5*cos(6.28318*(vec3(0.0,0.33,0.67)+angle/6.28318+uTime*0.035));
        c=mix(c,c*(0.60+rainbow*0.95),uTrip*(0.07+cornerMask*(0.17+pulse*0.16)));
        vec2 ghostUV=clamp(uv+vec2(sin(uTime*0.61),cos(uTime*0.47))*0.018*uTrip,0.0,1.0);
        c=mix(c,texture(uTex,ghostUV).bgr,uTrip*0.13);
        float bands=0.92+0.08*sin(radius*62.0-uTime*1.1+sin(angle*9.0));c*=mix(1.0,bands,uTrip);
    } else c=texture(uTex,uv).rgb;
    if(uPcp>0.001){float lum=dot(c,vec3(0.299,0.587,0.114));c=mix(vec3(lum),c,1.0+uPcp*2.8);c=(c-0.5)*(1.0+uPcp*0.38)+0.5;c+=vec3(0.10,-0.035,0.08)*uPcp*(0.5+0.5*sin(uTime*1.7));c=clamp(c,0.0,1.0);}
    if (uTonemap > 0.001) {
        vec2 px = 1.0 / max(uResolution, vec2(1.0));
        vec3 neighbours = (texture(uTex, clamp(uv + vec2(px.x, 0.0), 0.0, 1.0)).rgb +
                           texture(uTex, clamp(uv - vec2(px.x, 0.0), 0.0, 1.0)).rgb +
                           texture(uTex, clamp(uv + vec2(0.0, px.y), 0.0, 1.0)).rgb +
                           texture(uTex, clamp(uv - vec2(0.0, px.y), 0.0, 1.0)).rgb) * 0.25;
        c = clamp(c + (c - neighbours) * (0.16 * uTonemap), 0.0, 1.0);
        c = mix(c, aces(c), uTonemap * 0.4);
        float gradedLum = dot(c, vec3(0.299, 0.587, 0.114));
        c = mix(vec3(gradedLum), c, 1.055);
        c += vec3(0.010, 0.003, -0.007) * smoothstep(0.35, 0.90, gradedLum);
        vec2 vignettePos = screenUV * 2.0 - 1.0;
        float vignette = 1.0 - 0.065 * smoothstep(0.38, 1.45, dot(vignettePos, vignettePos));
        c *= mix(1.0, vignette, uTonemap);
        float dither = fract(sin(dot(screenUV * uResolution, vec2(12.9898, 78.233))) * 43758.5453) - 0.5;
        c += dither * (uTonemap / 255.0);
        c = clamp(c, 0.0, 1.0);
    }
    if(uDungeonExposure<0.0)c*=mix(1.0,0.22,clamp(-uDungeonExposure,0.0,1.0));
    else if(uDungeonExposure>0.0)c=mix(c,vec3(1.0),clamp(uDungeonExposure,0.0,1.0)*0.58);
    float g = dot(c, vec3(0.299, 0.587, 0.114));
    FragColor = vec4(mix(c, vec3(g), clamp(uMono, 0.0, 1.0)), 1.0);
}
