3

我正在按照 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);
}

然而结果并不令人满意。遮挡缓冲区大部分都是白色的,不显示任何遮挡。但是,如果我非常靠近一个物体,我会看到一些奇怪的类似噪音的结果,如下所示:

奇怪的 SSAO 视觉结果

这显然是不正确的。我已经进行了相当多的调试,并相信所有相关变量都正确传递(它们都可视化为颜色)。我在视图空间中进行计算。

我将简要介绍我采取的步骤(和选择),以防你们中的任何人认为其中一个步骤出现问题。

视图空间位置/法线 John Chapman 使用视图光线和线性化深度值检索视图空间位置。由于我使用的延迟渲染器已经具有每个片段的世界空间位置,因此我只需将它们与视图矩阵相乘以将它们放到视图空间中。

我对法线向量采用类似的方法。我从缓冲区纹理中获取世界空间法线向量,将它们转换为 [-1,1] 范围,并将它们与视图矩阵的 transpose(inverse(mat3(..))) 相乘。

视图空间位置和法线可视化如下:

视图空间和法线可视化 SSAO

这对我来说是正确的。

围绕法线定向半球 创建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] 之间是随机的,并将它们转换为片段的当前视图空间位置(原点)。

然后我从线性化深度缓冲区中采样(当靠近一个物体时,我在下面看到它):

深度缓冲区线性化

最后将采样的深度值与当前片段的深度值进行比较并添加遮挡值。请注意,我不执行范围检查,因为我不认为这是导致这种行为的原因,我宁愿现在尽可能地减少它。

我不知道是什么导致了这种行为。我相信它在采样深度值的某个地方。据我所知,我在正确的坐标系中工作,线性化的深度值也在视图空间中,并且所有变量的设置都有些正确。

4

0 回答 0