I followed this tutorial for Screen-Space Ambient Occlusion (SSAO), but it seems like something is not working as it should.
As you can see from the screenshot, there is some pattern in the darker areas where ambient occlusion is most evident.
I suspect there's something wrong with the sample kernel, but I followed the tutorial rigidly. The only difference is that I also consider resizing the window and the movement of the camera.
These are the relevant code snippets.
SSAO vertex shader
#version 450 core
out VertData
{
smooth vec2 texcoord;
} vert;
void main()
{
switch (gl_VertexID)
{
case 0:
vert.texcoord = vec2(0.0f, 0.0f);
break;
case 1:
vert.texcoord = vec2(0.0f, 1.0f);
break;
case 2:
vert.texcoord = vec2(1.0f, 0.0f);
break;
case 3:
vert.texcoord = vec2(1.0f, 1.0f);
break;
}
vec2 position = vert.texcoord * 2.0f - 1.0f;
gl_Position = vec4(position, 0.0f, 1.0f);
}
SSAO fragment shader
#version 450 core
struct FragData {
vec3 position;
vec3 normal;
vec3 noise;
};
uniform sampler2D position;
uniform sampler2D normal;
uniform sampler2D KernelTexture;
uniform sampler2D noise;
in VertData
{
smooth vec2 texcoord;
} vert;
out float occlusion;
uniform int KernelSize;
uniform vec2 NoiseScale;
uniform float radius;
uniform float bias;
uniform mat4 projection;
void main()
{
FragData frag;
frag.position = texture(position, vert.texcoord).xyz;
frag.normal = normalize(texture(normal, vert.texcoord).xyz);
frag.noise = normalize(texture(noise, vert.texcoord * NoiseScale).xyz);
// TBN matrix
vec3 tangent = normalize(frag.noise - frag.normal * dot(frag.noise, frag.normal));
vec3 bitangent = cross(frag.normal, tangent);
mat3 TBN = mat3(tangent, bitangent, frag.normal);
occlusion = 0.0f;
for(int i = 0; i < KernelSize; i++)
{
float x = float(i) / float(KernelSize);
for(int j = 0; j < KernelSize; j++)
{
float y = float(j) / float(KernelSize);
vec3 MySample = TBN * texture(KernelTexture, vec2(x, y)).xyz;
MySample = frag.position + MySample * radius;
vec4 offset = vec4(MySample, 1.0f);
offset = projection * offset;
offset.xyz /= offset.w;
offset.xyz = offset.xyz * 0.5f + 0.5f;
float depth = texture(position, offset.xy).z;
float range = smoothstep(0.0f, 1.0f, radius / abs(frag.position.z - depth));
occlusion += ((depth >= MySample.z + bias) ? 1.0f : 0.0f) * range;
}
}
occlusion = 1.0f - (occlusion / float(KernelSize * KernelSize));
}
kernel configuration
void setKernel()
{
auto lerp = [] (float a, float b, float f)
{
return a + f * (b - a);
};
kernel.clear();
for (int i = 0; i < KernelSize * KernelSize; i++)
{
float x = RandomZeroOne();
float y = RandomZeroOne();
float z = RandomZeroOne();
QVector3D sample = { x * 2.0f - 1.0f, y * 2.0f - 1.0f, z };
sample.normalize();
sample *= RandomZeroOne();
float scale = static_cast<float>(i) / static_cast<float>(KernelSize);
scale = lerp(0.1f, 1.0f, scale * scale);
sample *= scale;
kernel.append(sample);
}
}
void setKernelTexture()
{
glBindTexture(GL_TEXTURE_2D, textures[TextureIndex::KERNEL]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, KernelSize, KernelSize, 0, GL_RGB, GL_FLOAT, &(kernel[0]));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
==================== UPDATE ====================
I think I found the problem. I didn't update NoiseScale when the window is resized.
void resizeGL(int w, int h)
{
/* ... */
// now updating NoiseScale
NoiseScale.setX(static_cast<float>(geometry().width()));
NoiseScale.setY(static_cast<float>(geometry().height()));
NoiseScale /= static_cast<float>(NoiseSize);
/* ... */
}
And this is the result:
But now performance is very bad. With a 64-sample kernel and a 4x4 noise matrix, I get a frame rate of 3-4 fps.
How can I improve performance? Thanks!