declare const _default: "\n    #include \"screenDepthPS\"\n\n    varying uv0: vec2f;\n\n    uniform uInvResolution: vec2f;\n    uniform uAspect: f32;\n\n    // Largely based on 'Dominant Light Shadowing'\n    // 'Lighting Technology of The Last of Us Part II' by Hawar Doghramachi, Naughty Dog, LLC\n\n    fn getWFromProjectionMatrix(p: mat4x4f, v: vec3f) -> f32 {\n        // this essentially returns (p * vec4(v, 1.0)).w, but we make some assumptions\n        // this assumes a perspective projection\n        return -v.z;\n        // this assumes a perspective or ortho projection\n        // return p[2][3] * v.z + p[3][3];\n    }\n\n    fn getViewSpaceZFromW(p: mat4x4f, w: f32) -> f32 {\n        // this assumes a perspective projection\n        return -w;\n        // this assumes a perspective or ortho projection\n        // return (w - p[3][3]) / p[2][3];\n    }\n\n    const kLog2LodRate: f32 = 3.0;\n\n    // random number between 0 and 1, using interleaved gradient noise\n    fn random(w: vec2f) -> f32 {\n        const m: vec3f = vec3f(0.06711056, 0.00583715, 52.9829189);\n        return fract(m.z * fract(dot(w, m.xy)));\n    }\n\n    // returns the frag coord in the GL convention with (0, 0) at the bottom-left\n    fn getFragCoord() -> vec2f {\n        return pcPosition.xy;\n    }\n\n    fn computeViewSpacePositionFromDepth(uv: vec2f, linearDepth: f32) -> vec3f {\n        return vec3f((0.5 - uv) * vec2f(uniform.uAspect, 1.0) * linearDepth, linearDepth);\n    }\n\n    fn faceNormal(dpdx: vec3f, dpdy: vec3f) -> vec3f {\n        return normalize(cross(dpdx, dpdy));\n    }\n\n    // Compute normals using derivatives, which essentially results in half-resolution normals\n    // this creates artifacts around geometry edges.\n    // Note: when using the spirv optimizer, this results in much slower execution time because\n    //       this whole expression is inlined in the AO loop below.\n    fn computeViewSpaceNormalDeriv(position: vec3f) -> vec3f {\n        return faceNormal(dpdx(position), dpdy(position));\n    }\n\n    // Compute normals directly from the depth texture, resulting in full resolution normals\n    // Note: This is actually as cheap as using derivatives because the texture fetches\n    //       are essentially equivalent to textureGather (which we don't have on ES3.0),\n    //       and this is executed just once.\n    fn computeViewSpaceNormalDepth(position: vec3f, uv: vec2f) -> vec3f {\n        let uvdx: vec2f = uv + vec2f(uniform.uInvResolution.x, 0.0);\n        let uvdy: vec2f = uv + vec2f(0.0, uniform.uInvResolution.y);\n        let px: vec3f = computeViewSpacePositionFromDepth(uvdx, -getLinearScreenDepth(uvdx));\n        let py: vec3f = computeViewSpacePositionFromDepth(uvdy, -getLinearScreenDepth(uvdy));\n        let dpdx: vec3f = px - position;\n        let dpdy: vec3f = py - position;\n        return faceNormal(dpdx, dpdy);\n    }\n\n    // Ambient Occlusion, largely inspired from:\n    // 'The Alchemy Screen-Space Ambient Obscurance Algorithm' by Morgan McGuire\n    // 'Scalable Ambient Obscurance' by Morgan McGuire, Michael Mara and David Luebke\n\n    uniform uSampleCount: vec2f;\n    uniform uSpiralTurns: f32;\n\n    const PI: f32 = 3.14159;\n\n    fn tapLocation(i: f32, noise: f32) -> vec3f {\n        let offset: f32 = ((2.0 * PI) * 2.4) * noise;\n        let angle: f32 = ((i * uniform.uSampleCount.y) * uniform.uSpiralTurns) * (2.0 * PI) + offset;\n        let radius: f32 = (i + noise + 0.5) * uniform.uSampleCount.y;\n        return vec3f(cos(angle), sin(angle), radius * radius);\n    }\n\n    fn startPosition(noise: f32) -> vec2f {\n        let angle: f32 = ((2.0 * PI) * 2.4) * noise;\n        return vec2f(cos(angle), sin(angle));\n    }\n\n    uniform uAngleIncCosSin: vec2f;\n\n    fn tapAngleStep() -> mat2x2f {\n        let t: vec2f = uniform.uAngleIncCosSin;\n        return mat2x2f(vec2f(t.x, t.y), vec2f(-t.y, t.x));\n    }\n\n    fn tapLocationFast(i: f32, p: vec2f, noise_in: f32) -> vec3f {\n        let radius: f32 = (i + noise_in + 0.5) * uniform.uSampleCount.y;\n        return vec3f(p.x, p.y, radius * radius);\n    }\n\n    uniform uMaxLevel: f32;\n    uniform uInvRadiusSquared: f32;\n    uniform uMinHorizonAngleSineSquared: f32;\n    uniform uBias: f32;\n    uniform uPeak2: f32;\n\n    fn computeAmbientOcclusionSAO(occlusion_ptr: ptr<function, f32>, i: f32, ssDiskRadius: f32,\n            uv: vec2f, origin: vec3f, normal: vec3f,\n            tapPosition: vec2f, noise: f32) {\n\n        let tap: vec3f = tapLocationFast(i, tapPosition, noise);\n\n        let ssRadius: f32 = max(1.0, tap.z * ssDiskRadius); // at least 1 pixel screen-space radius\n\n        let uvSamplePos: vec2f = uv + (ssRadius * tap.xy) * uniform.uInvResolution;\n\n        // TODO: level is not used, but could be used with mip-mapped depth texture\n        let level: f32 = clamp(floor(log2(ssRadius)) - kLog2LodRate, 0.0, uniform.uMaxLevel);\n        let occlusionDepth: f32 = -getLinearScreenDepth(uvSamplePos);\n        let p: vec3f = computeViewSpacePositionFromDepth(uvSamplePos, occlusionDepth);\n\n        // now we have the sample, compute AO\n        let v: vec3f = p - origin;        // sample vector\n        let vv: f32 = dot(v, v);       // squared distance\n        let vn: f32 = dot(v, normal);  // distance * cos(v, normal)\n\n        // discard samples that are outside of the radius, preventing distant geometry to cast\n        // shadows -- there are many functions that work and choosing one is an artistic decision.\n        var w_val: f32 = max(0.0, 1.0 - vv * uniform.uInvRadiusSquared);\n        w_val = w_val * w_val;\n\n        // discard samples that are too close to the horizon to reduce shadows cast by geometry\n        // not sufficiently tessellated. The goal is to discard samples that form an angle 'beta'\n        // smaller than 'epsilon' with the horizon. We already have dot(v,n) which is equal to the\n        // sin(beta) * |v|. So the test simplifies to vn^2 < vv * sin(epsilon)^2.\n        w_val = w_val * step(vv * uniform.uMinHorizonAngleSineSquared, vn * vn);\n\n        *occlusion_ptr = *occlusion_ptr + w_val * max(0.0, vn + origin.z * uniform.uBias) / (vv + uniform.uPeak2);\n    }\n\n    uniform uProjectionScaleRadius: f32;\n    uniform uIntensity: f32;\n    uniform uRandomize: f32;\n\n    fn scalableAmbientObscurance(uv: vec2f, origin: vec3f, normal: vec3f) -> f32 {\n        let noise: f32 = random(getFragCoord()) + uniform.uRandomize;\n        var tapPosition: vec2f = startPosition(noise);\n        let angleStep: mat2x2f = tapAngleStep();\n\n        // Choose the screen-space sample radius\n        // proportional to the projected area of the sphere\n        let ssDiskRadius: f32 = -(uniform.uProjectionScaleRadius / origin.z);\n\n        var occlusion: f32 = 0.0;\n        for (var i: i32 = 0; i < i32(uniform.uSampleCount.x); i = i + 1) {\n            computeAmbientOcclusionSAO(&occlusion, f32(i), ssDiskRadius, uv, origin, normal, tapPosition, noise);\n            tapPosition = angleStep * tapPosition;\n        }\n        return occlusion;\n    }\n\n    uniform uPower: f32;\n\n    @fragment\n    fn fragmentMain(input: FragmentInput) -> FragmentOutput {\n        var output: FragmentOutput;\n\n        let uv: vec2f = input.uv0; // interpolated to pixel center\n\n        let depth: f32 = -getLinearScreenDepth(input.uv0);\n        let origin: vec3f = computeViewSpacePositionFromDepth(uv, depth);\n        let normal: vec3f = computeViewSpaceNormalDepth(origin, uv);\n\n        var occlusion: f32 = 0.0;\n        if (uniform.uIntensity > 0.0) {\n            occlusion = scalableAmbientObscurance(uv, origin, normal);\n        }\n\n        // occlusion to visibility\n        var ao: f32 = max(0.0, 1.0 - occlusion * uniform.uIntensity);\n        ao = pow(ao, uniform.uPower);\n\n        output.color = vec4f(ao, ao, ao, 1.0);\n        return output;\n    }\n";
export default _default;
