我按照John Chapman(http://john-chapman-graphics.blogspot.nl/2013/01/ssao-tutorial.html)的教程在延迟渲染器中实现SSAO。 SSAO着色器的输入缓冲区为:
我首先列出完整的着色器,然后简要介绍一下步骤:
#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]范围,并将它们与视图矩阵的转置(逆(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]之间是随机的,并将它们转换为片段的当前视图空间位置(原点) )。
然后我从线性化的深度缓冲区中进行采样(当我靠近一个物体时,我可以在下面看到它):
最后将采样的深度值与当前片段的深度值进行比较并添加遮挡值。请注意,我没有进行范围检查,因为我不相信这是导致这种行为的原因,而且我现在要尽可能保持最小化。
我不知道造成这种行为的原因。我相信这是在深度值采样的某个地方。据我所知,我在正确的坐标系中工作,线性化的深度值也在视图空间中,并且所有变量都设置得恰到好处。