/**
 * NOTE: This file is autogenerated. Do not edit.
 */
export const ssaocalculatorShader = {
    vertex: (options) => /* glsl */ `
`,
    fragment: (options) => /* glsl */ `
        precision highp float;
        varying vec2 vUV;
        
        // Samplers
        uniform highp sampler2D depthSampler;
        uniform sampler2D noiseSampler;
        uniform vec2 noiseScale;
        uniform vec3 sampleKernel[sampleKernelSize];
        uniform mat4 projectionMatrix;
        
        uniform float strength;
        uniform float radius;
        uniform vec2 nearFar;
        
        float bias = -0.015;
        
        // #include ViewSpacePosition.glsl
        uniform mat4 invertedProjectionMatrix;
        
        vec3 viewSpacePositionFromDepth(float depth, float u, float v)
        {
            float z = depth;
            // Get x/w and y/w from the viewport position
            float x = u * 2.0 - 1.0;
            float y = v * 2.0 - 1.0;
            vec4 vProjectedPos = vec4(x, y, z, 1.0);
            // Transform by the inverse projection matrix
            vec4 viewspacePosition = invertedProjectionMatrix * vProjectedPos;
            // Divide by w to get the view-space position
            return viewspacePosition.xyz / viewspacePosition.w;
        }
        // #include NormalsFromDepth.glsl
        uniform vec2 depthDimensions;
        
        // Fetch and unpack depth value at the given pixel coordinate
        float fetchDepthAt(ivec2 pixelCoord) {
            vec4 rgba = texelFetch(depthSampler, pixelCoord, 0);
            return rgba.x;
        }
        
        // Get texture size directly from the sampler - avoids precision issues in depthDimensions uniform
        ivec2 getDepthTextureSize() {
            return textureSize(depthSampler, 0);
        }
        
        #ifdef expensiveNormals
        
            // https://wickedengine.net/2019/09/22/improved-normal-reconstruction-from-depth/
            // Constructs the normal from a 4 sample neighbor cross
            // Takes the smallest differences in depth in the vertical and horizontal directions from the fragment center and recreates the viewspace positions with the corresponding texel
            // Then these two positions are used to create two vectors from the fragment center, and these two vectors are crossed to get ther surface normal in viewspace
            vec3 expensiveViewSpaceNormalFromNeighbours(vec2 uv, vec3 viewSpaceFragmentCenter, float depthCenter) {
                // Use gl_FragCoord for the center pixel - exact integer position, no UV conversion issues
                // Note: For neighbor calls from compare(), uv is offset but we compute pixel from uv
                ivec2 texSize = getDepthTextureSize();
                vec2 texSizeF = vec2(texSize);
                ivec2 pixelCenter = ivec2(uv * texSizeF);
        
                // Sample neighbors using integer offsets
                float depthRight = fetchDepthAt(pixelCenter + ivec2(1, 0));
                float depthUp = fetchDepthAt(pixelCenter + ivec2(0, 1));
                float depthDown = fetchDepthAt(pixelCenter + ivec2(0, -1));
                float depthLeft = fetchDepthAt(pixelCenter + ivec2(-1, 0));
        
                // Calculate neighbor UVs from INTEGER pixel coordinates
                // Use texture size directly to avoid precision issues in depthDimensions uniform
                vec2 right = (vec2(pixelCenter + ivec2(1, 0)) + 0.5) / texSizeF;
                vec2 up = (vec2(pixelCenter + ivec2(0, 1)) + 0.5) / texSizeF;
                vec2 down = (vec2(pixelCenter + ivec2(0, -1)) + 0.5) / texSizeF;
                vec2 left = (vec2(pixelCenter + ivec2(-1, 0)) + 0.5) / texSizeF;
        
                // Epsilon bias prevents flip-flopping on flat surfaces where depth differences
                // are nearly equal. Without this, floating-point precision differences between
                // GPU drivers (especially Linux/Android vs Windows) cause the comparison to
                // inconsistently flip between adjacent pixels, creating horizontal banding artifacts.
                float epsilon = 0.001;
                bool downIsLess = abs(depthDown - depthCenter) < abs(depthUp - depthCenter) + epsilon;
                bool leftIsLess = abs(depthLeft - depthCenter) < abs(depthRight - depthCenter) + epsilon;
        
                vec3 point1;
                vec3 point2;
        
                if (downIsLess && leftIsLess) {
                    point1 = viewSpacePositionFromDepth(depthLeft, left.x, left.y);
                    point2 = viewSpacePositionFromDepth(depthDown, down.x, down.y);
                } else if (downIsLess) { // right is less
                    point1 = viewSpacePositionFromDepth(depthDown, down.x, down.y);
                    point2 = viewSpacePositionFromDepth(depthRight, right.x, right.y);
                } else if (!downIsLess && leftIsLess) {
                    point1 = viewSpacePositionFromDepth(depthUp, up.x, up.y);
                    point2 = viewSpacePositionFromDepth(depthLeft, left.x, left.y);
                } else { // right is less, up is less
                    point1 = viewSpacePositionFromDepth(depthRight, right.x, right.y);
                    point2 = viewSpacePositionFromDepth(depthUp, up.x, up.y);
                }
        
                return -normalize(cross(normalize(point1 - viewSpaceFragmentCenter), normalize(point2 - viewSpaceFragmentCenter)));
            }
        #else
            // Take the fragment above and to the right of the current fragment and use their depths to reconstruct two positions, subtract these positions with
            // the fragment centers position to get two vectors, take the cross product of these two vectors to get the viewspace surface normal
            vec3 viewSpaceNormalFromNeighbours(vec2 uv, vec3 viewSpaceFragmentCenter) {
                // Convert UV to pixel coordinates for precise sampling
                ivec2 texSize = getDepthTextureSize();
                vec2 texSizeF = vec2(texSize);
                ivec2 pixelCenter = ivec2(uv * texSizeF);
        
                // Sample neighbors using integer offsets
                float depthRight = fetchDepthAt(pixelCenter + ivec2(1, 0));
                float depthUp = fetchDepthAt(pixelCenter + ivec2(0, 1));
        
                // Calculate neighbor UVs from INTEGER pixel coordinates to avoid floating-point precision issues
                vec2 right = (vec2(pixelCenter + ivec2(1, 0)) + 0.5) / texSizeF;
                vec2 up = (vec2(pixelCenter + ivec2(0, 1)) + 0.5) / texSizeF;
        
                vec3 rightViewSpaceFragment = viewSpacePositionFromDepth(depthRight, right.x, right.y);
                vec3 upViewSpaceFragment = viewSpacePositionFromDepth(depthUp, up.x, up.y);
        
                return -normalize(cross(normalize(rightViewSpaceFragment - viewSpaceFragmentCenter), normalize(upViewSpaceFragment - viewSpaceFragmentCenter)));
            }
        #endif
        
        float ambientOcclusionValue(vec3 viewSpaceFragment, vec3 viewSpaceNormal, vec2 screenUV, vec2 texSizeF) {
            vec3 noise = texture2D(noiseSampler, screenUV * noiseScale).rgb;
            vec3 randomVec = noise * 2.0 - 1.0;
            float occlusion = 0.0;
        
            vec3 tangent = normalize(randomVec - viewSpaceNormal * dot(randomVec, viewSpaceNormal));
            vec3 biTangent = cross(viewSpaceNormal, tangent);
            mat3 tbn = mat3(tangent, biTangent, viewSpaceNormal);
        
            for (int i = 0; i < sampleKernelSize; i++) {
                vec3 samplePosition = tbn * sampleKernel[i];
                samplePosition = samplePosition * radius + viewSpaceFragment;
        
                vec4 offset = vec4(samplePosition, 1.0);
                offset = projectionMatrix * offset;
                offset.xy /= offset.w;
                offset.xy = offset.xy * 0.5 + vec2(0.5);
        
                // Use texelFetch for precise depth sampling (texSizeF cached outside loop)
                ivec2 samplePixelCoord = ivec2(offset.xy * texSizeF);
                float viewDepth = fetchDepthAt(samplePixelCoord);
                // Use sample's offset UV, not screenUV
                float sampleDepth = viewSpacePositionFromDepth(viewDepth, offset.x, offset.y).z;
        
                float rangeCheck = smoothstep(0.0, 1.0, radius / abs(viewSpaceFragment.z - sampleDepth));
                occlusion += (sampleDepth <= samplePosition.z + bias ? 1.0 * rangeCheck : 0.0);
            }
        
            occlusion = min((occlusion / float(sampleKernelSize)) * strength, 1.0);
        
            return occlusion;
        }
        
        void main(void)
        {
            vec2 sampleUV = vUV;
            // Use texelFetch for precise depth sampling - bypasses UV precision issues on Linux/Android
            ivec2 texSize = getDepthTextureSize();
            vec2 texSizeF = vec2(texSize);
            ivec2 pixelCoord = ivec2(sampleUV * texSizeF);
            float fragmentDepth = fetchDepthAt(pixelCoord);
            vec3 viewSpacePosition = viewSpacePositionFromDepth(fragmentDepth, sampleUV.x, sampleUV.y);
            #ifdef expensiveNormals
                vec3 viewSpaceNormal = expensiveViewSpaceNormalFromNeighbours(sampleUV, viewSpacePosition, fragmentDepth);
            #else
                vec3 viewSpaceNormal = viewSpaceNormalFromNeighbours(sampleUV, viewSpacePosition);
            #endif
        
            // Check both upper bound (>= 0.9995 means far plane/no geometry)
            // AND lower bound (<= 0.0001 means cleared to black/no geometry written)
            float fragmentWritten = step(0.0001, fragmentDepth) * (1.0 - step(0.9995, fragmentDepth));
            float ambientOcclusion = 0.0;
            if (fragmentWritten == 1.0) {
                float distanceFade = 1.0 - smoothstep(nearFar.y * 0.1, nearFar.y * 0.9, viewSpacePosition.z);
                float depthDifferenceScaling = 1.0 + pow((1.0 - (viewSpacePosition.z / nearFar.y)), 16.0) * 5.0;
                float aoContribution = distanceFade * depthDifferenceScaling;
                ambientOcclusion = ambientOcclusionValue(viewSpacePosition, viewSpaceNormal, sampleUV, texSizeF) * aoContribution;
            }
        
            gl_FragColor = vec4(vec3(ambientOcclusion), 1.0);
        }`
};
//# sourceMappingURL=SSAOCalculatorShader.js.map