SSAO没有显示正确的结果,大多数没有可见的遮挡

时间:2015-04-06 10:54:28

标签: c++ opengl glsl ssao

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

但结果并不令人满意。遮挡缓冲区大部分都是白色的,并且不显示任何遮挡。但是,如果我移动得非常靠近物体,我可以看到一些奇怪的噪音效果,如下所示:

weird SSAO visual results

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

我将简要介绍一下我采取的步骤(以及选择),以防任何一个人在其中一个步骤中出现问题。

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

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

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

view-space and normals visualized 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]之间是随机的,并将它们转换为片段的当前视图空间位置(原点) )。

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

depth buffer linearized

最后将采样的深度值与当前片段的深度值进行比较并添加遮挡值。请注意,我没有进行范围检查,因为我不相信这是导致这种行为的原因,而且我现在要尽可能保持最小化。

我不知道造成这种行为的原因。我相信这是在深度值采样的某个地方。据我所知,我在正确的坐标系中工作,线性化的深度值也在视图空间中,并且所有变量都设置得恰到好处。

0 个答案:

没有答案