3
$\begingroup$

I'm having problems with Ambient Occlusion. I've tried to follow both John Chapman's improvements over the Crytek AO and LearnOpenGL's tutorial of it when I failed to properly obtain the correct result. https://i.sstatic.net/fhm8V.jpg

As the camera turns or moves, the screen flashes consistently though it seems arbitrary. Can anyone tell me what I'm doing wrong?

#version 140

in vec2 UV;
in vec4 vRay;
out vec3 SSAOout;

const int kernelSize = 64;

uniform mat4 projection;
uniform sampler2D gbuffer0;
uniform sampler2D gbuffer1;
uniform sampler2D gbuffer2;
uniform sampler2D gbuffer3;
uniform sampler2D noiseTex;
uniform vec3 kernels[kernelSize];

vec4 ViewPosFromDepth(float depth, vec2 TexCoord) {
    float z = depth * 2.0 - 1.0;

    vec4 clipSpacePosition = vec4(TexCoord * 2.0 - 1.0, z, 1.0);
    vec4 viewSpacePosition = invProjMat * clipSpacePosition;
    viewSpacePosition /= viewSpacePosition.w;

    return viewSpacePosition;
}

const vec2 noiseScale = vec2(1366.0/4.0, 768.0/4.0);

const float near = 0.1;
const float far = 100.0;

float LinearizeDepth(float depth)
{
    float z = depth * 2.0 - 1.0; // back to NDC 
    return (2.0 * near * far) / (far + near - z * (far - near));    
}

void main() {
    vec4 gbuffer0Val = texture(gbuffer0, UV);
    vec4 gbuffer1Val = texture(gbuffer1, UV);
    vec4 gbuffer2Val = texture(gbuffer2, UV);
    vec4 gbuffer3Val = texture(gbuffer3, UV);

    vec3 fragPos    = ViewPosFromDepth(gbuffer0Val.r, UV).rgb;
    float dist      = LinearizeDepth(gbuffer0Val.r);
    vec3 normal     = gbuffer1Val.rgb;
    normal          = decodeNormal(normal);
    vec3 randomVec  = vec3(2 * texture(noiseTex, UV * noiseScale).rg - 1, 0); 

    vec3 tangent   = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 TBN       = mat3(tangent, bitangent, normal);

    float bias = 0.025;
    float radius = 3.0;
    float occlusion = 0.0;

    for(int i = 0; i < kernelSize; i++) {
        vec3 sampleKernel = TBN * kernels[i];
        sampleKernel = fragPos + sampleKernel * radius;

        vec4 offset = vec4(sampleKernel, 1.0);
        offset      = projection * offset;    // from view to clip-space
        offset.xyz /= offset.w;               // perspective divide
        offset.xyz  = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0

        float sampleDepth = texture(gbuffer0, 1-offset.xy).r;
        sampleDepth = LinearizeDepth(sampleDepth);
        float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
        occlusion += (sampleDepth >= sampleKernel.z + bias ? 1.0 : 0.0) * rangeCheck;
    }

    occlusion = 1.0 - (occlusion / kernelSize);
    SSAOout = vec3(occlusion);
}
$\endgroup$

1 Answer 1

2
$\begingroup$

You need to be careful about spaces - position and normal need to be in view space.

To transform depth into view space position I used this method. So:

vec3 viewPositionFromDepth(vec2 vTexCoord)
{
    float z = texture2D(depthTexture, vTexCoord).r;
    // Get x/w and y/w from the viewport position
    float x = vTexCoord.x * 2.0 - 1.0;
    float y = vTexCoord.y * 2.0 - 1.0;
    vec4 vProjectedPos = vec4(x, y, z, 1.0f);
    // Transform by the inverse projection matrix
    vec4 vPositionVS = invProjection*vProjectedPos;
    // Divide by w to get the view-space position
    return vPositionVS.xyz / vPositionVS.w;
}

To transform normal into view space you need to multiply it by transpose of inverse of view matrix (should be done on CPU):

vec4 n = transpose(inverse(ViewMatrix)) * normalize(texture2D(normalTexture, texCoord)*2.0 - 1.0 );

And in loop while sampling depth with offset to compare with kernel position, this depth should be in same space as position so I did it the same way:

float sampleDepth = viewPositionFromDepth(offset.xy).z;

This is not the most optimal way though, but it worked for me.

$\endgroup$
1
  • $\begingroup$ My issue was that my normals were in world space. I thought otherwise, but realized when you posted this. Also, You don't necessarily need 2.0 - 1.0 in the normal retrieval section if your normals are signed. $\endgroup$ Commented Jun 3, 2017 at 23:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.