我正在按照 John Chapman 的教程 ( http://john-chapman-graphics.blogspot.nl/2013/01/ssao-tutorial.html ) 在延迟渲染器中实现 SSAO。SSAO 着色器的输入缓冲区是:
- 以线性化深度作为 w 分量的世界空间位置。
- 世界空间法线向量
- 噪声 4x4 纹理
我将首先列出完整的着色器,然后简要介绍这些步骤:
#version 330 core
in VS_OUT {
vec2 TexCoords;
} fs_in;
uniform sampler2D texPosDepth;
uniform sampler2D texNormalSpec;
uniform sampler2D texNoise;
uniform vec3 samples[64];
uniform mat4 projection;
uniform mat4 view;
uniform mat3 viewNormal; // transpose(inverse(mat3(view)))
const vec2 noiseScale = vec2(800.0f/4.0f, 600.0f/4.0f);
const float radius = 5.0;
void main( void )
{
float linearDepth = texture(texPosDepth, fs_in.TexCoords).w;
// Fragment's view space position and normal
vec3 fragPos_World = texture(texPosDepth, fs_in.TexCoords).xyz;
vec3 origin = vec3(view * vec4(fragPos_World, 1.0));
vec3 normal = texture(texNormalSpec, fs_in.TexCoords).xyz;
normal = normalize(normal * 2.0 - 1.0);
normal = normalize(viewNormal * normal); // Normal from world to view-space
// Use change-of-basis matrix to reorient sample kernel around origin's normal
vec3 rvec = texture(texNoise, fs_in.TexCoords * noiseScale).xyz;
vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
// Loop through the sample kernel
float occlusion = 0.0;
for(int i = 0; i < 64; ++i)
{
// get sample position
vec3 sample = tbn * samples[i]; // From tangent to view-space
sample = sample * radius + origin;
// project sample position (to sample texture) (to get position on screen/texture)
vec4 offset = vec4(sample, 1.0);
offset = projection * offset;
offset.xy /= offset.w;
offset.xy = offset.xy * 0.5 + 0.5;
// get sample depth
float sampleDepth = texture(texPosDepth, offset.xy).w;
// range check & accumulate
// float rangeCheck = abs(origin.z - sampleDepth) < radius ? 1.0 : 0.0;
occlusion += (sampleDepth <= sample.z ? 1.0 : 0.0);
}
occlusion = 1.0 - (occlusion / 64.0f);
gl_FragColor = vec4(vec3(occlusion), 1.0);
}
然而结果并不令人满意。遮挡缓冲区大部分都是白色的,不显示任何遮挡。但是,如果我非常靠近一个物体,我会看到一些奇怪的类似噪音的结果,如下所示:
这显然是不正确的。我已经进行了相当多的调试,并相信所有相关变量都正确传递(它们都可视化为颜色)。我在视图空间中进行计算。
我将简要介绍我采取的步骤(和选择),以防你们中的任何人认为其中一个步骤出现问题。
视图空间位置/法线 John Chapman 使用视图光线和线性化深度值检索视图空间位置。由于我使用的延迟渲染器已经具有每个片段的世界空间位置,因此我只需将它们与视图矩阵相乘以将它们放到视图空间中。
我对法线向量采用类似的方法。我从缓冲区纹理中获取世界空间法线向量,将它们转换为 [-1,1] 范围,并将它们与视图矩阵的 transpose(inverse(mat3(..))) 相乘。
视图空间位置和法线可视化如下:
这对我来说是正确的。
围绕法线定向半球
创建tbn
矩阵的步骤与 John Chapman 的教程中描述的相同。我创建噪声纹理如下:
std::vector<glm::vec3> ssaoNoise;
for (GLuint i = 0; i < noise_size; i++)
{
glm::vec3 noise(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f);
noise = glm::normalize(noise);
ssaoNoise.push_back(noise);
}
...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]);
我可以在片段着色器中可视化噪声,这样似乎可以工作。
样本深度 我将所有样本从切线转换到视图空间(样本在 xy 轴上的 [-1,1] 和 z 轴上的 [0,1] 之间是随机的,并将它们转换为片段的当前视图空间位置(原点)。
然后我从线性化深度缓冲区中采样(当靠近一个物体时,我在下面看到它):
最后将采样的深度值与当前片段的深度值进行比较并添加遮挡值。请注意,我不执行范围检查,因为我不认为这是导致这种行为的原因,我宁愿现在尽可能地减少它。
我不知道是什么导致了这种行为。我相信它在采样深度值的某个地方。据我所知,我在正确的坐标系中工作,线性化的深度值也在视图空间中,并且所有变量的设置都有些正确。