
#include "/lib/taa/res_params.glsl"
#include "/lib/uniforms.glsl"
#define waterfog
varying vec2 texcoord;
uniform vec3 atmosphereUp;
uniform vec3 cameraPosition;
uniform float far;

uniform sampler2D depthtex0;
uniform sampler2D depthtex1;
uniform sampler2D colortex1;

uniform sampler2D colortex0;
uniform sampler2D colortex3;
uniform sampler2D colortex4;
uniform sampler2D colortex7;
uniform sampler2D colortex8;
uniform sampler2D colortex11;

uniform float aspectRatio;
uniform float frameTimeCounter;
uniform int isEyeInWater;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferModelViewInverse;

#include "/lib/color_transforms.glsl"
vec3 toScreenSpace(vec3 position)
{
    position = position * 2.0 - 1.0;
    position.xy = vec2(gbufferProjectionInverse[0].x, gbufferProjectionInverse[1].y) * position.xy +
                  gbufferProjectionInverse[3].xy;
    return vec3(position.xy, gbufferProjectionInverse[3].z) /
           (gbufferProjectionInverse[2].w * position.z + gbufferProjectionInverse[3].w);
}
#ifdef VOLUMETRIC_LIGHTING

#include "/lib/fog/fog3.glsl"
#include "/lib/fog/fogdh.glsl"
#else

#include "/lib/fog/fog3_org.glsl"
#include "/lib/fog/fogdh_org.glsl"
#endif
uniform float near;
float ld(const float dist)
{
    return (2.0 * near) / (far + near - dist * (far - near));
}
/////

/////

#include "/lib/ssaodh.glsl"

vec3 getDepthPoint(vec2 coord, float depth)
{
    vec4 pos;
    pos.xy = coord;
    pos.z = depth;
    pos.w = 1.0;
    pos.xyz = pos.xyz * 2.0 - 1.0; // convert from the 0-1 range to the -1 to +1 range
    pos = gbufferProjectionInverse * pos;
    pos.xyz /= pos.w;

    return pos.xyz;
}

vec3 constructNormal(float depthA, vec2 texcoords, sampler2D depthtex)
{
    const vec2 offsetB = vec2(0.0, 0.001);
    const vec2 offsetC = vec2(0.001, 0.0);

    float depthB = texture(depthtex, texcoords + offsetB).r;
    float depthC = texture(depthtex, texcoords + offsetC).r;

    vec3 A = getDepthPoint(texcoords, depthA);
    vec3 B = getDepthPoint(texcoords + offsetB, depthB);
    vec3 C = getDepthPoint(texcoords + offsetC, depthC);

    vec3 AB = normalize(B - A);
    vec3 AC = normalize(C - A);

    vec3 normal = -cross(AB, AC);
    // normal.z = -normal.z;

    return normalize(normal);
}
vec2 computeRefractedCoord(vec3 fragPos, vec3 fragPos0, mat4 gbufferModelViewInverse, vec3 cameraPosition, sampler2D colortex7, sampler2D colortex3, vec2 texcoord, vec2 refractedCoord)
{
    float estimatedDepth = clamp(exp(-0.5 * distance(fragPos, fragPos0)), 0.75, 1.0);
    vec3 np3 = mat3(gbufferModelViewInverse) * fragPos + gbufferModelViewInverse[3].xyz + cameraPosition;

    float norm = 0.0;
    const mat2 rotationMatrix = mat2(vec2(-0.737367, -0.675493), vec2(0.675493, -0.737367));
    const vec2 waveSize[4] = vec2[](vec2(64.0), vec2(32.0, 16.0), vec2(16.0, 32.0), vec2(48.0));

    for (int i = 0; i < 4; i++)
    {
        norm += texture2D(colortex7, mod(rotationMatrix * (np3.xz + np3.y) / waveSize[i], 1.0)).x;
    }

    float displ = clamp(norm / (estimatedDepth * 512.0), 0.0, 1.0);
    refractedCoord += displ;

    if (texture(colortex3, refractedCoord).a <= 0.95)
    {
        refractedCoord = texcoord;
    }

    return refractedCoord;
}

void main()
{
#ifdef TAA
    /* DRAWBUFFERS:0 */
#else
    /* DRAWBUFFERS:4 */
#endif

    vec2 refractedCoord = texcoord;
    bool isWater = texture(colortex3, refractedCoord).r > 0.5;
    float z0 = texture(depthtex0, refractedCoord).x;
    bool isHand = z0 < 0.6;
    vec3 fragPos = toScreenSpace(vec3(refractedCoord, z0));
    float z1 = texture(depthtex1, refractedCoord).x;
    vec3 fragPos0 = toScreenSpace(vec3(refractedCoord, z1));

#if defined DISTANT_HORIZONS
    vec4 DH = texture(colortex5, refractedCoord);
    float z0DH = texture(dhDepthTex0, refractedCoord).x;
    float z1DH = texture(dhDepthTex1, refractedCoord).x;
    vec3 fragPosDH = toScreenSpaceDH(vec3(refractedCoord, z0DH));
    vec3 fragPosDH0 = toScreenSpaceDH(vec3(refractedCoord, z1DH));
#endif

#ifdef REFRACTION
    if (isWater && !isHand)
    {
        refractedCoord = computeRefractedCoord(fragPos, fragPos0, gbufferModelViewInverse, cameraPosition, colortex7, colortex3, texcoord, refractedCoord);

        z1 = texture(depthtex1, refractedCoord).x;
        fragPos0 = toScreenSpace(vec3(refractedCoord, z1));
    }
#endif

    vec3 color = texture(colortex0, refractedCoord.xy).rgb;
    vec2 lightmap = decodeVec2(texelFetch(colortex11, ivec2(gl_FragCoord.xy), 0).b);

#ifdef CAUSTICS
    if ((isEyeInWater == 0 && isWater) || (isEyeInWater == 1 && !isWater))
    {
        vec3 p3 = mat3(gbufferModelViewInverse) * fragPos0 + gbufferModelViewInverse[3].xyz + cameraPosition;
        float caustics = waterCaustics(p3, lightPos);
        vec3 lightCaustics = min(clamp(sunLight, 0.0, 1.5) * (0.5 + max(pow(caustics * 2.0, 2.0), 0.0)), lightmap.y * 2.0);
        color *= lightCaustics;
    }
#endif

    vec4 translucents = texture(colortex1, refractedCoord.xy).rgba;
    float borderFogFactor = GetBorderFogMixFactor(fragPos, far, 0.0);
    vec3 sky = texture(colortex8, refractedCoord).rgb;
    float z = z0;

#if defined DISTANT_HORIZONS
    z = (z >= 1.0) ? z0DH : z;
    translucents = (DH.a > 0.0) ? mix(translucents, DH, borderFogFactor) : translucents;
    fragPos = (z0 >= 1.0) ? fragPosDH : fragPos;
    fragPos0 = (z1 >= 1.0) ? fragPosDH0 : fragPos0;
#endif

    if (z < 1 || (isEyeInWater == 1 && !isWater))
    {
        color = getVolumetricRays3(color.rgb, translucents, sky, borderFogFactor, fragPos, fragPos0, isWater, clamp(lightmap.x + lightmap.y, 0.1, 1));
    }

#if defined DISTANT_HORIZONS && defined SKYBLEND
    borderFogFactor = (borderFogFactor > 0.0) ? GetBorderFogMixFactorDH(fragPosDH, dhFarPlane) : borderFogFactor;
    color = mix(color, sky, borderFogFactor * borderFogFactor);
#endif

    color *= exp(-length(fragPos / 2) * mix(darknessFactor, blindness, blindness));
    gl_FragData[0].rgb = vec3(color);
}
