uniform ivec2 atlasSize;

const float P = 1.324717957244746025960908854;
float frameR2X = fract(0.5 + frameCounter / P);
float frameR2Y = fract(0.5 + frameCounter / (P * P));
vec2 taaOffset = vec2((frameR2X * 2.0 - 1.0) / viewWidth, (frameR2Y * 2.0 - 1.0) / viewHeight);

const float mincoord = 1.0 / 4096.0;

const float MAX_OCCLUSION_DISTANCE = MAX_DIST;
const float MIX_OCCLUSION_DISTANCE = MAX_DIST * 0.9;
const int MAX_OCCLUSION_POINTS = MAX_ITERATIONS;

varying vec4 vtexcoordam; // .st for add, .pq for mul
varying vec4 vtexcoord;

vec2 dcdx = dFdx(vtexcoord.st * vtexcoordam.pq) * exp2(0);
vec2 dcdy = dFdy(vtexcoord.st * vtexcoordam.pq) * exp2(0);

vec4 readNormal(in vec2 coord) {
    return texture2DGradARB(normals, fract(coord) * vtexcoordam.pq + vtexcoordam.st, dcdx, dcdy);
}
vec4 readTexture(in vec2 coord) {
    return texture2DGradARB(texture, fract(coord) * vtexcoordam.pq + vtexcoordam.st, dcdx, dcdy);
}
vec2 pomColor(vec2 uv, vec3 fragpos, float noise, mat3 tbn) {

    vec3 coord = vec3(vtexcoord.st, 1.0);
    vec2 adjustedTexCoord = uv;
    vec3 viewVector = normalize(tbn * fragpos);
    float dist = length(fragpos);
    gl_FragDepth = gl_FragCoord.z;

    if (dist < MAX_OCCLUSION_DISTANCE) {

        if (viewVector.z < 0.0 && readNormal(vtexcoord.st).a < 0.9999 && readNormal(vtexcoord.st).a > 0.00001) {
            vec3 interval = viewVector.xyz / -viewVector.z / MAX_OCCLUSION_POINTS * POM_DEPTH;
            vec2 atlasAspect = vec2(atlasSize.y / float(atlasSize.x), atlasSize.x / float(atlasSize.y));
            vec2 viewCorrection = max(vec2((vtexcoordam.q) / (vtexcoordam.p) * atlasAspect.x, 1.0), vec2(1.0, (vtexcoordam.p) / (vtexcoordam.q) * atlasAspect.y));
            interval.xy *= viewCorrection;

            coord += noise * interval;
            float sumVec = 1;
            float numLayers = mix(MAX_OCCLUSION_POINTS, 16, max(dot(vec3(0.0, 0.0, 1.0), fragpos), 0.0));

            for (int loopCount = 0; (loopCount < numLayers) &&
                    (1.0 - POM_DEPTH + POM_DEPTH * readNormal(coord.st).a < coord.p) && coord.p >= 0.0; ++loopCount) {
                coord = coord + interval;
                sumVec += 1.0;
            }

            if (coord.t < mincoord) {
                if (readTexture(vec2(coord.s, mincoord)).a == 0.0) {

                    coord.t = mincoord;

                    discard;
                }
            }

            adjustedTexCoord = mix(fract(coord.st) * vtexcoordam.pq + vtexcoordam.st, adjustedTexCoord, max(dist - MIX_OCCLUSION_DISTANCE, 0.0) / (MAX_OCCLUSION_DISTANCE - MIX_OCCLUSION_DISTANCE));

            vec3 truePos = fragpos + sumVec * inverse(tbn) * interval;
            gl_FragDepth = toClipSpace3(truePos).z;
        }
    }
    return adjustedTexCoord;
}