我正在尝试在我的游戏引擎中实现高质量的 SSAO,为此我使用 OpenGL 作为主要渲染 API 和 GLSL 着色器。到目前为止,结果还不错,说实话,我实际上实现了 https://learnopengl.com/Advanced-Lighting/SSAO 这篇文章中的 SSAO。所以我上手并没有遇到太多困难,而且代码与文章非常相似。我唯一的问题是我在各个场景对象周围出现的白色发光边缘伪影。
就像边缘在流血。我尝试过 SSAO 的其他实现,但这个工件总是出现。其他的好像都还好。
此外,我没有对 SSAO 结果进行任何后模糊操作来平滑结果。我听说做模糊实际上会导致这种类型的伪像更多......
这是片段/像素着色器代码:
#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;
}
这是我生成样本的方法:
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);
我还应该提到,所有 g 缓冲区纹理(gPosition、gNormal、gNoise)均采用 GL_RGBA32F 格式,并将过滤器设置为 GL_NEAREST。位置和法线都在视图空间中。
注意:我的引擎还支持 DirectX 11 渲染 API,并且我还使用 HLSL 实现了相同的 SSAO 技术,不幸的是,这是同样的问题。除了物体周围有白色边缘外,一切都很好。