3

I followed the tutorial at Learn OpenGL to implement Screenspace Ambient Occlusion. Things are mostly looking okay besides a strange artifact at the top and bottom of the window.

The problem is more obvious moving the camera, when it appears as if top parts of the image are imprinted on the bottom and vise versa, as shown in this video.

enter image description here

The artifact worsens when standing close to a wall and looking up and down so perhaps the Znear value is contributing? The scale of my scene does seem small compared to other demos, Znear and Zfar are 0.01f and 1000 and the width of the shown hallway is around 1.2f.

I've read into the common SSAO artifacts and haven't found anything resembling this.

#version 330 core

in vec2 TexCoords;
layout (location = 0) out vec3 FragColor;   

uniform sampler2D MyTexture0;   // Position
uniform sampler2D MyTexture1;   // Normal
uniform sampler2D MyTexture2;   // TexNoise

const int samples = 64;
const float radius = 0.25;
const float bias = 0.025;

uniform mat4 projectionMatrix;

uniform float screenWidth;
uniform float screenHeight;

void main()
{
    //tile noise texture over screen based on screen dimensions divided by noise size
    vec2 noiseScale = vec2(screenWidth/4.0, screenHeight/4.0); 

    vec3 sample_sphere[64];
    sample_sphere[0] = vec3(0.04977, -0.04471, 0.04996);
    sample_sphere[1] = vec3(0.01457, 0.01653, 0.00224);
    sample_sphere[2] = vec3(-0.04065, -0.01937, 0.03193);
    sample_sphere[3] = vec3(0.01378, -0.09158, 0.04092);
    sample_sphere[4] = vec3(0.05599, 0.05979, 0.05766);
    sample_sphere[5] = vec3(0.09227, 0.04428, 0.01545);
    sample_sphere[6] = vec3(-0.00204, -0.0544, 0.06674);
    sample_sphere[7] = vec3(-0.00033, -0.00019, 0.00037);
    sample_sphere[8] = vec3(0.05004, -0.04665, 0.02538);
    sample_sphere[9] = vec3(0.03813, 0.0314, 0.03287);
    sample_sphere[10] = vec3(-0.03188, 0.02046, 0.02251);
    sample_sphere[11] = vec3(0.0557, -0.03697, 0.05449);
    sample_sphere[12] = vec3(0.05737, -0.02254, 0.07554);
    sample_sphere[13] = vec3(-0.01609, -0.00377, 0.05547);
    sample_sphere[14] = vec3(-0.02503, -0.02483, 0.02495);
    sample_sphere[15] = vec3(-0.03369, 0.02139, 0.0254);
    sample_sphere[16] = vec3(-0.01753, 0.01439, 0.00535);
    sample_sphere[17] = vec3(0.07336, 0.11205, 0.01101);
    sample_sphere[18] = vec3(-0.04406, -0.09028, 0.08368);
    sample_sphere[19] = vec3(-0.08328, -0.00168, 0.08499);
    sample_sphere[20] = vec3(-0.01041, -0.03287, 0.01927);
    sample_sphere[21] = vec3(0.00321, -0.00488, 0.00416);
    sample_sphere[22] = vec3(-0.00738, -0.06583, 0.0674);
    sample_sphere[23] = vec3(0.09414, -0.008, 0.14335);
    sample_sphere[24] = vec3(0.07683, 0.12697, 0.107);
    sample_sphere[25] = vec3(0.00039, 0.00045, 0.0003);
    sample_sphere[26] = vec3(-0.10479, 0.06544, 0.10174);
    sample_sphere[27] = vec3(-0.00445, -0.11964, 0.1619);
    sample_sphere[28] = vec3(-0.07455, 0.03445, 0.22414);
    sample_sphere[29] = vec3(-0.00276, 0.00308, 0.00292);
    sample_sphere[30] = vec3(-0.10851, 0.14234, 0.16644);
    sample_sphere[31] = vec3(0.04688, 0.10364, 0.05958);
    sample_sphere[32] = vec3(0.13457, -0.02251, 0.13051);
    sample_sphere[33] = vec3(-0.16449, -0.15564, 0.12454);
    sample_sphere[34] = vec3(-0.18767, -0.20883, 0.05777);
    sample_sphere[35] = vec3(-0.04372, 0.08693, 0.0748);
    sample_sphere[36] = vec3(-0.00256, -0.002, 0.00407);
    sample_sphere[37] = vec3(-0.0967, -0.18226, 0.29949);
    sample_sphere[38] = vec3(-0.22577, 0.31606, 0.08916);
    sample_sphere[39] = vec3(-0.02751, 0.28719, 0.31718);
    sample_sphere[40] = vec3(0.20722, -0.27084, 0.11013);
    sample_sphere[41] = vec3(0.0549, 0.10434, 0.32311);
    sample_sphere[42] = vec3(-0.13086, 0.11929, 0.28022);
    sample_sphere[43] = vec3(0.15404, -0.06537, 0.22984);
    sample_sphere[44] = vec3(0.05294, -0.22787, 0.14848);
    sample_sphere[45] = vec3(-0.18731, -0.04022, 0.01593);
    sample_sphere[46] = vec3(0.14184, 0.04716, 0.13485);
    sample_sphere[47] = vec3(-0.04427, 0.05562, 0.05586);
    sample_sphere[48] = vec3(-0.02358, -0.08097, 0.21913);
    sample_sphere[49] = vec3(-0.14215, 0.19807, 0.00519);
    sample_sphere[50] = vec3(0.15865, 0.23046, 0.04372);
    sample_sphere[51] = vec3(0.03004, 0.38183, 0.16383);
    sample_sphere[52] = vec3(0.08301, -0.30966, 0.06741);
    sample_sphere[53] = vec3(0.22695, -0.23535, 0.19367);
    sample_sphere[54] = vec3(0.38129, 0.33204, 0.52949);
    sample_sphere[55] = vec3(-0.55627, 0.29472, 0.3011);
    sample_sphere[56] = vec3(0.42449, 0.00565, 0.11758);
    sample_sphere[57] = vec3(0.3665, 0.00359, 0.0857);
    sample_sphere[58] = vec3(0.32902, 0.0309, 0.1785);
    sample_sphere[59] = vec3(-0.08294, 0.51285, 0.05656);
    sample_sphere[60] = vec3(0.86736, -0.00273, 0.10014);
    sample_sphere[61] = vec3(0.45574, -0.77201, 0.00384);
    sample_sphere[62] = vec3(0.41729, -0.15485, 0.46251);
    sample_sphere[63] = vec3 (-0.44272, -0.67928, 0.1865);

    // get input for SSAO algorithm
    vec3 fragPos = texture(MyTexture0, TexCoords).xyz;
    vec3 normal = normalize(texture(MyTexture1, TexCoords).rgb);
    vec3 randomVec = normalize(texture(MyTexture2, TexCoords * noiseScale).xyz);

    // create TBN change-of-basis matrix: from tangent-space to view-space
    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 TBN = mat3(tangent, bitangent, normal);

    // iterate over the sample kernel and calculate occlusion factor
    float occlusion = 0.0;
    for(int i = 0; i < samples; ++i)
    {
        // get sample position
        vec3 sample = TBN * sample_sphere[i]; // from tangent to view-space
        sample = fragPos + sample * radius; 

        // project sample position (to sample texture) (to get position on screen/texture)
        vec4 offset = vec4(sample, 1.0);
        offset = projectionMatrix * 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

        // get sample depth
        float sampleDepth = texture(MyTexture0, offset.xy).z;

        // range check & accumulate
        float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
        occlusion += (sampleDepth >= sample.z + bias ? 1.0 : 0.0) * rangeCheck;           
    }
    occlusion = 1.0 - (occlusion / samples);

    FragColor = vec3(occlusion);
}
livin_amuk
  • 1,285
  • 12
  • 26
  • 1
    The SSAO algorithm samples around each fragment ([Monte Carlo integration](https://en.wikipedia.org/wiki/Monte_Carlo_integration)). At the borders of the view the coordinates of the of some samples are out of bounds. This may cause the artifacts. If you are close to a wall then then the radius for the samples gets larger than the size of the projection to the viewport. The samples are out of bounds, too. – Rabbid76 Mar 05 '19 at 07:18
  • That makes a lot of sense. Do you know why might I be experiencing a more exaggerated effect than the tutorial? or what the remedy is? – livin_amuk Mar 05 '19 at 07:24
  • The algorithm is a bit sensitive and strongly depends on you need. I implemented bound checks and played around with the distribution of the samples. But not, every check goes at cost of performance. – Rabbid76 Mar 05 '19 at 07:34

1 Answers1

5

As Rabbid76 suggested, the artifacts were caused by sampling outside of the screen borders. I added a check to prevent this and things are looking much better..

vec4 clipSpacePos = projectionMatrix * vec4(sample, 1.0); // from view to clip-space
vec3 ndcSpacePos = clipSpacePos.xyz /= clipSpacePos.w; // perspective divide
vec2 windowSpacePos = ((ndcSpacePos.xy + 1.0) / 2.0) * vec2(screenWidth, screenHeight);

if ((windowSpacePos.y > 0) && (windowSpacePos.y < screenHeight))
    if ((windowSpacePos.x > 0) && (windowSpacePos.x < screenWidth))
        // THEN APPLY AMBIENT OCCLUSION

enter image description here

It hasn't entirely fixed the issue though as areas close to the windows edge now appear lighter than they should because fewer samples are tested. Perhaps somebody can suggest an approach that moves the sample area to an appropriate location?

livin_amuk
  • 1,285
  • 12
  • 26
  • Thanks for posting your solution. I took another path and set the texture wrap mode for the gbuffers to Clamp_To_Border and used a color that disables ssao – Tyron Jul 16 '20 at 07:11
  • Conditionals in shaders are not the best idea. Readers are advised to use `clamp` instead. – polkovnikov.ph May 15 '23 at 02:32