I'm trying to implement a high quality SSAO in my game engine for which I'm using OpenGL as my primary rendering API and GLSL shaders. So far, the result is not bad and to be honest I actually implemented the SSAO from https://learnopengl.com/Advanced-Lighting/SSAO this article. So I didn't have much trouble getting myself started and the codes are pretty similar to the article. My only problem is the white glowing edge artifact I'm getting around my individual scene objects.
It's like the edges are bleeding. I have tried some other implemention of SSAO and this artifact always appear. Everything else seem to be fine.
Also I didn't do any Post-Blur thing against the SSAO result to smooth the result. I heard doing the blur actually causes this type artifacts more...
Here is the fragment/pixel shader code:
#version 460 core
in vec2 textureCoordinate;
layout(binding = 0) uniform sampler2D gPosition;
layout(binding = 1) uniform sampler2D gNormal;
layout(binding = 2) uniform sampler2D gNoise;
layout(std140, binding = 0) uniform CBPerFrame {
mat4 proj;
mat4 view;
mat4 world;
mat4 lightviewproj;
vec4 lightPosition;
vec4 cameraPosition;
float hasNormalMap;
float enableSSAO;
vec2 pad;
vec4 pad2;
} cbPerFrame;
const int samples = 64;
const vec2 noiseScale = vec2(1200.0/4.0, 800.0/4.0);
layout(std140, binding = 1) uniform CBSSAO {
vec3 sample_sphere[samples];
} cbSSAO;
void main() {
vec3 fragPos = texture(gPosition, textureCoordinate).xyz;
vec3 normal = normalize(texture(gNormal, textureCoordinate).rgb*2.0 - 1.0);
vec3 randomVec = texture(gNoise, textureCoordinate * noiseScale).xyz;
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 TBN = mat3(tangent, bitangent, normal);
float radius = 0.9f;
float bias = 0.05f;
float occlusion = 0.0;
for(int i = 0; i < samples; ++i) {
// get sample position
vec3 samplePos = TBN * cbSSAO.sample_sphere[i]; // from tangent to view-space
samplePos = fragPos + samplePos * radius;
vec4 offset = vec4(samplePos, 1.0);
offset = cbPerFrame.proj * 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(gPosition, offset.xy).z;
float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck;
}
occlusion = 1.0 - (occlusion / float(samples));
gl_FragColor = vec4(1, 1, 1, 1) * occlusion;
}
And here is how I generate the samples:
IGPUConstantBuffer* cbSSAO = rnd->createGPUConstantBuffer(sizeof(SSAOData));
std::uniform_real_distribution<float> randomFloats(0.0, 1.0); // random floats between [0.0, 1.0]
std::default_random_engine generator;
for (unsigned int i = 0; i < numSamples; ++i)
{
glm::vec3 sample(
randomFloats(generator) * 2.0 - 1.0,
randomFloats(generator) * 2.0 - 1.0,
randomFloats(generator)
);
sample = glm::normalize(sample);
sample *= randomFloats(generator);
float scale = (float)i / float(numSamples);
scale = lerp(0.1f, 1.0f, scale * scale);
sample *= scale;
SSAOData.samples[i] = sample;
}
cbSSAO->updateData(&SSAOData);
I should also mention that all the g-buffer textures (gPosition, gNormal, gNoise) are in GL_RGBA32F format with filter set to GL_NEAREST. Both the Positions and Normals are in view space.
Note: My engine also supports DirectX 11 rendering API and I also have implemented the same SSAO technique with HLSL and unfortunately, it's the same problem. Everything is just fine except that white edges around object.