
float dbao()
{
    float dither = blueNoise(gl_FragCoord.xy);
    float depth = texture(depthtex2, texcoord).r;
    const int samples = 4;
    const float piSquared = PI * PI;
    float twoNear = 2.0 * near;
    float farMinusNear = far - near;
    float farPlusNear = far + near;
    float twoFar = 2.0 * far;
    float invAspectRatio = 1.0 / aspectRatio;
    float gbufferProj = gbProjection_proxy[1][1];

    float ao = 0.0;
    float d = twoNear / (farPlusNear - depth * farMinusNear);
    vec2 scale = (piSquared * dither * vec2(invAspectRatio, 1.0) * gbufferProj) / (piSquared * far * d);

    for (int i = 1; i <= samples; i++)
    {
        float x = i + dither;
        float n = fract(x * 1.414) * PI;
        vec2 offset = vec2(cos(n), sin(n)) * x / samples * scale;

        vec2 texCoordOffsetPlus = texcoord.xy + offset;
        vec2 texCoordOffsetMinus = texcoord.xy - offset;

        float depthTexPlus = texture2D(depthtex0, texCoordOffsetPlus).r;
        float depthTexMinus = texture2D(depthtex0, texCoordOffsetMinus).r;

        float sd1 = twoNear / (farPlusNear - depthTexPlus * farMinusNear);
        float sd2 = twoNear / (farPlusNear - depthTexMinus * farMinusNear);

        float sample1 = (d - sd1) * twoFar;
        float sample2 = (d - sd2) * twoFar;

        float angle1 = clamp(0.5 - sample1, 0.0, 1.0);
        float angle2 = clamp(0.5 - sample2, 0.0, 1.0);

        float dist1 = clamp(0.25 * sample1 - 1.0, 0.0, 1.0);
        float dist2 = clamp(0.25 * sample2 - 1.0, 0.0, 1.0);

        ao += clamp(angle1 + dist1 + angle2 + dist2, 0.0, 1.0);
    }

    return ao / samples;
}

float rtao(vec3 N, vec3 P)
{

    /* constants ------------------------------------------------------ */
    const float PHI = 1.61803398875;
    const float TWO_PI = 6.28318530718;

    const int RAYS = 4;

    const float DEPTH_EPS = 0.007;
    const float SQRT3 = 1.73205080757;
    const float RAY_MIN = 0.0;
    const float RAY_MAX = PI * 0.75;

    /* tangent frame -------------------------------------------------- */
    vec3 nV = worldToView(N);
    vec3 up = (abs(nV.z) < 0.999) ? vec3(0, 0, 1) : vec3(1, 0, 0);
    vec3 T = normalize(cross(up, nV));
    vec3 B = cross(nV, T);

    float dith = blueNoise(gl_FragCoord.xy);

    float selfBias = mix(0.001, 0.01, clamp(P.z / far, 0.0, 1.0));

    float occ = 0.0; // accumulated occlusion

    /* ray loop ------------------------------------------------------- */
    for (int r = 0; r < RAYS; ++r)
    {
        /* Vogel hemisphere sample ------------------------------------ */
        float idx = float(r) + dith;
        float u = idx * (1.0 / float(RAYS));
        float phi = TWO_PI * fract(idx * PHI);
        float cosA = u;
        float sinA = sqrt(max(0.0, 1.0 - cosA * cosA));

        vec3 dir = normalize(T * (sinA * cos(phi)) + B * (sinA * sin(phi)) + nV * cosA);

        /* frustum-bound length --------------------------------------- */
        float maxLen = (dir.z > 0.0) ? (SQRT3 * far - P.z) / dir.z : (-SQRT3 * near - P.z) / dir.z;
        maxLen = abs(maxLen);

        float lenBeg = RAY_MIN;
        float lenEnd = min(RAY_MAX, maxLen);
        float rayLen = lenEnd - lenBeg;

        /* ─────── stochastic 1-tap trace ───────────────────────────── */

        /* pick ONE random point along the segment -------------------- */
        float tRay = (fract(idx * PI - dith) /* extra hash */);
        vec3 posW = P + dir * (lenBeg + tRay * rayLen);
        vec3 clip = toClipSpace3(posW);

        // Convert [0,1] clip coordinates to screen pixel coordinates
        vec2 screenPos = clip.xy * vec2(viewWidth, viewHeight);
        // Screen edge rejection: skip if outside screen bounds
        if (screenPos.x < 0.0 || screenPos.x > viewWidth || screenPos.y < 0.0 || screenPos.y > viewHeight)
            continue;

        float zs = ld(texelFetch(depthtex2, ivec2(screenPos), 0).r);
        float zr = ld(clip.z);
        float dz = zr - zs;

        float tol = DEPTH_EPS;
        float hitWeight = RAY_MAX / rayLen; // ≥1 for short rays

        if (dz > selfBias && dz <= tol)
            occ += hitWeight;
    }
    /* binary accumulation → divide by RAYS */
    float ao = clamp(1.0 - occ / float(RAYS), 0.0, 1.0);

    return ao;
}
