Hey, guys!
I tried to implement SSAO yesterday and I thought it was working, but then I realized it wasn’t
Basically what’s happening is that as the camera moves away from the object, the object begins to fade to white, but when I move closer, the object gets darker and darker.
I think I know what the problem is. Basically, when I move closer, the depth values of the fragments decrease, so it gets darker. But when I move further away, the depth values of the fragments increase, so it appears to be whiter. But that isn’t how SSAO is supposed to work.
Here are some pics:
https://anuj-rao.tinytake.com/sf/OTUxMzkyXzM5ODk2OTM
^When the camera is close to the Stanford dragon model
https://anuj-rao.tinytake.com/sf/OTUxMzkzXzM5ODk2OTQ
^When the camera moves away from the Standford dragon model
You see, it doesn’t look right. Also, the corners are lighter than the actual surfaces??!!!
It looks all wrong!
Here’s my SSAO fragment shader:
#version 400 core
in vec2 passTextureCoords;
out vec4 fragColour;
uniform sampler2D gPositionDepth;
uniform sampler2D gNormals;
uniform sampler2D noiseTexture;
uniform vec3 samples[16];
uniform mat4 projMatrix;
const vec2 noiseScale = vec2(1280/4, 780/4);
void main(void){
int kernelSize = 16;
float radius = 1;
vec3 fragPos = texture(gPositionDepth, passTextureCoords).xyz;
vec3 normal = texture(gNormals, passTextureCoords).xyz;
vec3 randomVec = texture(noiseTexture, passTextureCoords * noiseScale).xyz;
normal = normalize(normal);
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
vec3 bitangent = normalize(cross(normal, tangent));
mat3 TBN = mat3(tangent, bitangent, normal);
float occlusion = 0;
for(int i = 0; i < kernelSize; i++){
vec3 samp = TBN * samples[i];
samp = fragPos + samp * radius;
vec4 offset = vec4(samp, 1);
offset = projMatrix * offset;
offset.xyz /= offset.w;
offset.xyz = offset.xyz * 0.5 + 0.5;
float sampleDepth = texture(gPositionDepth, offset.xy).w;
float rangeCheck = smoothstep(0, 1, radius / abs(fragPos.z - sampleDepth));
occlusion += (sampleDepth >= samp.z ? 1 : 0) * rangeCheck;
}
occlusion = 1 - (occlusion/16);
fragColour = vec4(vec3(pow(occlusion, 8)), 1);
}
G-buffer vertex shader:
#version 400 core
in vec3 positions;
in vec4 colour;
in vec3 normals;
out vec4 passColour;
out vec3 fragPos;
out vec3 passNormals;
uniform mat4 transMatrix;
uniform mat4 projMatrix;
uniform mat4 viewMatrix;
void main(void){
gl_Position = projMatrix * viewMatrix * transMatrix * vec4(positions, 1.0);
passColour = colour;
passNormals = (viewMatrix * transMatrix * vec4(normals, 0)).xyz;
fragPos = (viewMatrix * transMatrix * vec4(positions, 1.0)).xyz;
}
G-buffer fragment shader:
#version 400 core
in vec4 passColour;
in vec3 fragPos;
in vec3 passNormals;
layout (location = 0) out vec4 gPositionDepth;
layout (location = 1) out vec4 gNormals;
layout (location = 2) out vec4 gAlbedoSpec;
const float NEAR = 0.01f;
const float FAR = 1000;
float linearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0;
return (2.0 * NEAR * FAR) / (FAR + NEAR - z * (FAR - NEAR));
}
void main(void){
gPositionDepth.rgb = fragPos;
gPositionDepth.a = linearizeDepth(gl_FragCoord.z);
gNormals = vec4(normalize(passNormals), 1);
gAlbedoSpec = passColour;
}
And finally, here’s how I generate the hemispherical sample kernel and the random rotation vectors:
package renderer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import util.MathUtils;
public class SSAO {
public static List<Vector3f> getSSAOKernel(){
List<Vector3f> kernel = new ArrayList<Vector3f>();
for(int i = 0; i < 16; i++){
Vector3f sample = new Vector3f(((float)Math.random()) * 2 - 1, ((float)Math.random()) * 2 - 1, ((float)Math.random()));
sample.normalize();
sample.mul(((float)Math.random()));
float scale = 1f/16f;
scale = MathUtils.lerp(0.1f, 1f, scale * scale);
sample.mul(scale);
kernel.add(sample);
}
return kernel;
}
public static int createSSAONoiseTexture(){
List<Vector3f> ssaoNoise = new ArrayList<Vector3f>();
for(int i = 0; i < 16; i++){
Vector3f noise = new Vector3f(((float)Math.random()) * 2 - 1, ((float)Math.random()) * 2 - 1, 0);
noise.normalize();
ssaoNoise.add(noise);
}
int noisetexture = GL11.glGenTextures();
Loader.TEXTURE_LIST.add(noisetexture);
FloatBuffer buffer = BufferUtils.createFloatBuffer(ssaoNoise.size() * 3);
for(Vector3f v : ssaoNoise){
buffer.put(v.x);
buffer.put(v.y);
buffer.put(v.z);
}
buffer.flip();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, noisetexture);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_RGB16F, 4, 4, 0, GL11.GL_RGB, GL11.GL_FLOAT, buffer);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
return noisetexture;
}
}
I’m following the tutorial from learnopengl.com
A little help would really be appreciated. I’ve already scoured the Internet for answers and I can’t think of any myself. Thanks