OpenGL - 奇怪的SSAO伪影问题

3
我按照Learn OpenGL的教程来实现屏幕空间环境光遮蔽。除了窗口顶部和底部出现奇怪的伪影之外,其他都看起来还不错。
问题在移动相机时更加明显,似乎图像的顶部部分被印在底部,反之亦然,如视频所示。

enter image description here

当靠近墙壁并上下观察时,物品会恶化,因此可能 Znear 值有所贡献?与其他演示相比,我的场景规模似乎较小,Znear 和 Zfar 分别为 0.01f1000,显示的走廊宽度约为 1.2f
我已经研究了常见的 SSAO 瑕疵,并没有发现任何类似的情况。
#version 330 core

in vec2 TexCoords;
layout (location = 0) out vec3 FragColor;   

uniform sampler2D MyTexture0;   // Position
uniform sampler2D MyTexture1;   // Normal
uniform sampler2D MyTexture2;   // TexNoise

const int samples = 64;
const float radius = 0.25;
const float bias = 0.025;

uniform mat4 projectionMatrix;

uniform float screenWidth;
uniform float screenHeight;

void main()
{
    //tile noise texture over screen based on screen dimensions divided by noise size
    vec2 noiseScale = vec2(screenWidth/4.0, screenHeight/4.0); 

    vec3 sample_sphere[64];
    sample_sphere[0] = vec3(0.04977, -0.04471, 0.04996);
    sample_sphere[1] = vec3(0.01457, 0.01653, 0.00224);
    sample_sphere[2] = vec3(-0.04065, -0.01937, 0.03193);
    sample_sphere[3] = vec3(0.01378, -0.09158, 0.04092);
    sample_sphere[4] = vec3(0.05599, 0.05979, 0.05766);
    sample_sphere[5] = vec3(0.09227, 0.04428, 0.01545);
    sample_sphere[6] = vec3(-0.00204, -0.0544, 0.06674);
    sample_sphere[7] = vec3(-0.00033, -0.00019, 0.00037);
    sample_sphere[8] = vec3(0.05004, -0.04665, 0.02538);
    sample_sphere[9] = vec3(0.03813, 0.0314, 0.03287);
    sample_sphere[10] = vec3(-0.03188, 0.02046, 0.02251);
    sample_sphere[11] = vec3(0.0557, -0.03697, 0.05449);
    sample_sphere[12] = vec3(0.05737, -0.02254, 0.07554);
    sample_sphere[13] = vec3(-0.01609, -0.00377, 0.05547);
    sample_sphere[14] = vec3(-0.02503, -0.02483, 0.02495);
    sample_sphere[15] = vec3(-0.03369, 0.02139, 0.0254);
    sample_sphere[16] = vec3(-0.01753, 0.01439, 0.00535);
    sample_sphere[17] = vec3(0.07336, 0.11205, 0.01101);
    sample_sphere[18] = vec3(-0.04406, -0.09028, 0.08368);
    sample_sphere[19] = vec3(-0.08328, -0.00168, 0.08499);
    sample_sphere[20] = vec3(-0.01041, -0.03287, 0.01927);
    sample_sphere[21] = vec3(0.00321, -0.00488, 0.00416);
    sample_sphere[22] = vec3(-0.00738, -0.06583, 0.0674);
    sample_sphere[23] = vec3(0.09414, -0.008, 0.14335);
    sample_sphere[24] = vec3(0.07683, 0.12697, 0.107);
    sample_sphere[25] = vec3(0.00039, 0.00045, 0.0003);
    sample_sphere[26] = vec3(-0.10479, 0.06544, 0.10174);
    sample_sphere[27] = vec3(-0.00445, -0.11964, 0.1619);
    sample_sphere[28] = vec3(-0.07455, 0.03445, 0.22414);
    sample_sphere[29] = vec3(-0.00276, 0.00308, 0.00292);
    sample_sphere[30] = vec3(-0.10851, 0.14234, 0.16644);
    sample_sphere[31] = vec3(0.04688, 0.10364, 0.05958);
    sample_sphere[32] = vec3(0.13457, -0.02251, 0.13051);
    sample_sphere[33] = vec3(-0.16449, -0.15564, 0.12454);
    sample_sphere[34] = vec3(-0.18767, -0.20883, 0.05777);
    sample_sphere[35] = vec3(-0.04372, 0.08693, 0.0748);
    sample_sphere[36] = vec3(-0.00256, -0.002, 0.00407);
    sample_sphere[37] = vec3(-0.0967, -0.18226, 0.29949);
    sample_sphere[38] = vec3(-0.22577, 0.31606, 0.08916);
    sample_sphere[39] = vec3(-0.02751, 0.28719, 0.31718);
    sample_sphere[40] = vec3(0.20722, -0.27084, 0.11013);
    sample_sphere[41] = vec3(0.0549, 0.10434, 0.32311);
    sample_sphere[42] = vec3(-0.13086, 0.11929, 0.28022);
    sample_sphere[43] = vec3(0.15404, -0.06537, 0.22984);
    sample_sphere[44] = vec3(0.05294, -0.22787, 0.14848);
    sample_sphere[45] = vec3(-0.18731, -0.04022, 0.01593);
    sample_sphere[46] = vec3(0.14184, 0.04716, 0.13485);
    sample_sphere[47] = vec3(-0.04427, 0.05562, 0.05586);
    sample_sphere[48] = vec3(-0.02358, -0.08097, 0.21913);
    sample_sphere[49] = vec3(-0.14215, 0.19807, 0.00519);
    sample_sphere[50] = vec3(0.15865, 0.23046, 0.04372);
    sample_sphere[51] = vec3(0.03004, 0.38183, 0.16383);
    sample_sphere[52] = vec3(0.08301, -0.30966, 0.06741);
    sample_sphere[53] = vec3(0.22695, -0.23535, 0.19367);
    sample_sphere[54] = vec3(0.38129, 0.33204, 0.52949);
    sample_sphere[55] = vec3(-0.55627, 0.29472, 0.3011);
    sample_sphere[56] = vec3(0.42449, 0.00565, 0.11758);
    sample_sphere[57] = vec3(0.3665, 0.00359, 0.0857);
    sample_sphere[58] = vec3(0.32902, 0.0309, 0.1785);
    sample_sphere[59] = vec3(-0.08294, 0.51285, 0.05656);
    sample_sphere[60] = vec3(0.86736, -0.00273, 0.10014);
    sample_sphere[61] = vec3(0.45574, -0.77201, 0.00384);
    sample_sphere[62] = vec3(0.41729, -0.15485, 0.46251);
    sample_sphere[63] = vec3 (-0.44272, -0.67928, 0.1865);

    // get input for SSAO algorithm
    vec3 fragPos = texture(MyTexture0, TexCoords).xyz;
    vec3 normal = normalize(texture(MyTexture1, TexCoords).rgb);
    vec3 randomVec = normalize(texture(MyTexture2, TexCoords * noiseScale).xyz);

    // create TBN change-of-basis matrix: from tangent-space to view-space
    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 TBN = mat3(tangent, bitangent, normal);

    // iterate over the sample kernel and calculate occlusion factor
    float occlusion = 0.0;
    for(int i = 0; i < samples; ++i)
    {
        // get sample position
        vec3 sample = TBN * sample_sphere[i]; // from tangent to view-space
        sample = fragPos + sample * radius; 

        // project sample position (to sample texture) (to get position on screen/texture)
        vec4 offset = vec4(sample, 1.0);
        offset = projectionMatrix * 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

        // get sample depth
        float sampleDepth = texture(MyTexture0, offset.xy).z;

        // range check & accumulate
        float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
        occlusion += (sampleDepth >= sample.z + bias ? 1.0 : 0.0) * rangeCheck;           
    }
    occlusion = 1.0 - (occlusion / samples);

    FragColor = vec3(occlusion);
}

1
SSAO算法在每个片段周围采样(蒙特卡罗积分)。在视图边界处,一些样本的坐标超出了范围。这可能会导致伪影。如果您靠近墙壁,则样本的半径会变大,超过投影到视口的大小。样本也会超出范围。 - Rabbid76
这很有道理。你知道为什么我会比教程经历更夸张的效果吗?或者有什么解决方法吗? - livin_amuk
该算法有些敏感,且强烈依赖于您的需求。我实现了边界检查并尝试了样本分布。但是,每个检查都会牺牲一定的性能。 - Rabbid76
1个回答

5
正如Rabbid76所建议的那样,这些伪影是由于采样超出了屏幕边界而引起的。我添加了一个检查来防止这种情况发生,现在情况看起来好多了。
vec4 clipSpacePos = projectionMatrix * vec4(sample, 1.0); // from view to clip-space
vec3 ndcSpacePos = clipSpacePos.xyz /= clipSpacePos.w; // perspective divide
vec2 windowSpacePos = ((ndcSpacePos.xy + 1.0) / 2.0) * vec2(screenWidth, screenHeight);

if ((windowSpacePos.y > 0) && (windowSpacePos.y < screenHeight))
    if ((windowSpacePos.x > 0) && (windowSpacePos.x < screenWidth))
        // THEN APPLY AMBIENT OCCLUSION

enter image description here

然而,这并没有完全解决问题,因为靠近窗口边缘的区域现在看起来比应该亮一些,因为测试样本较少。也许有人可以建议一个将采样区移动到适当位置的方法?


感谢您发布您的解决方案。我选择了另一种方法,将纹理包裹模式设置为Clamp_To_Border,并使用了一种颜色来禁用ssao。 - Tyron
着色器中的条件语句并不是最好的选择。建议读者使用 clamp 代替。 - polkovnikov.ph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接