#version 330 core in vec2 vUV; out float FragColor; uniform sampler2D gDepth; uniform sampler2D gNormal; uniform sampler2D texNoise; uniform vec3 samples[64]; uniform mat4 proj; uniform mat4 invProj; uniform float radius; uniform float bias; uniform float noiseScale; uniform int kernelSize; uniform float power; vec3 getViewPos(vec2 uv) { float depth = texture(gDepth, uv).r; if (depth == 1.0) return vec3(0.0); float z = depth * 2.0 - 1.0; vec4 clip = vec4(uv * 2.0 - 1.0, z, 1.0); vec4 viewPos = invProj * clip; viewPos /= viewPos.w; return viewPos.xyz; } void main() { vec3 fragPos = getViewPos(vUV); if (fragPos == vec3(0.0)) { FragColor = 1.0; return; } vec3 normal = texture(gNormal, vUV).rgb; normal = normalize(normal * 2.0 - 1.0); vec3 randomVec = normalize(texture(texNoise, vUV * noiseScale).xyz * 2.0 - 1.0); vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal)); vec3 bitangent = cross(normal, tangent); mat3 TBN = mat3(tangent, bitangent, normal); float occlusion = 0.0; for (int i = 0; i < kernelSize; ++i) { vec3 sample = TBN * samples[i]; sample = fragPos + sample * radius; vec4 offset = proj * vec4(sample, 1.0); offset.xyz /= offset.w; vec2 sampleUV = offset.xy * 0.5 + 0.5; if (sampleUV.x < 0.0 || sampleUV.x > 1.0 || sampleUV.y < 0.0 || sampleUV.y > 1.0) continue; vec3 samplePos = getViewPos(sampleUV); if (samplePos == vec3(0.0)) continue; float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - samplePos.z)); float diff = samplePos.z - sample.z; if (diff > bias) occlusion += rangeCheck; } occlusion = 1.0 - (occlusion / float(kernelSize)); occlusion = pow(occlusion, power); FragColor = occlusion; }