<p>You need to be careful about spaces - position and normal need to be in view space.</p>

<p>To transform depth into view space position I used <a href="https://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/" rel="nofollow noreferrer">this</a> method.
So:</p>

<pre><code>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;
}
</code></pre>

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

<pre><code>vec4 n = transpose(inverse(ViewMatrix)) * normalize(texture2D(normalTexture, texCoord)*2.0 - 1.0 );
</code></pre>

<p>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:</p>

<pre><code>float sampleDepth = viewPositionFromDepth(offset.xy).z;
</code></pre>

<p>This is not the most optimal way though, but it worked for me.</p>

CC BY-SA 3.0
2017-06-03T23:15:01.397Z
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.
Karim Abdel Hamid
CC BY-SA 3.0
2017-06-02T13:56:28.060Z
2017-06-02T15:11:04.193Z
2017-06-02T15:11:04.193Z
mdkdy
<p>I'm having problems with Ambient Occlusion. I've tried to follow both <a href="http://john-chapman-graphics.blogspot.com.eg/2013/01/ssao-tutorial.html" rel="noreferrer">John Chapman's improvements over the Crytek AO</a> and <a href="https://learnopengl.com/#!Advanced-Lighting/SSAO" rel="noreferrer">LearnOpenGL's tutorial of it</a> when I failed to properly obtain the correct result. <a href="https://i.sstatic.net/fhm8V.jpg" rel="noreferrer">https://i.sstatic.net/fhm8V.jpg</a></p>

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

<pre><code>#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 &lt; 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 &gt;= sampleKernel.z + bias ? 1.0 : 0.0) * rangeCheck;
    }

    occlusion = 1.0 - (occlusion / kernelSize);
    SSAOout = vec3(occlusion);
}
</code></pre>

<opengl><glsl><ambient-occlusion>
Screen Space Ambient Occlusion Issues
Karim Abdel Hamid
# Screen Space Ambient Occlusion Issues

(Asked by: username_0 on 2017-06-01)

I'm having problems with Ambient Occlusion. I've tried to follow both [John Chapman's improvements over the Crytek AO](http://john-chapman-graphics.blogspot.com.eg/2013/01/ssao-tutorial.html) and [LearnOpenGL's tutorial of it](https://learnopengl.com/#!Advanced-Lighting/SSAO) 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);
}

```

**Tags:** opengl, glsl, ambient-occlusion


---

## 1 answers

### Answer by username_1 on 2017-06-02. Score: 2 | Accepted answer

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](https://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/) 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.

> **Comments:**
> **username_0**: 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.  

---

