为什么SSAO仅在某些角度/距离下工作?

公理

我试图在将LearnOpenGL SSAO教程用作资源时复制Sascha Willems SSAO示例但是我的SSAO代码仅部分覆盖了特定角度/距离的模型,并且在靠近对象时也具有非常强的自我遮挡效果。

左侧是我的渲染器,右侧是Sascha Willems SSAO示例

编辑:从RenderDoc的正确图像上有一些奇怪的伪像。对于那个很抱歉。

有关渲染器变量的一些注意事项:

  • 位置+深度图像使用VK_FORMAT_R32G32B32A32_SFLOAT格式,在RenderDoc中看起来正确。[ 1 ] [ 2 ]
  • 正常图像使用VK_FORMAT_R8G8B8A8_UNORM格式,并且在RenderDoc中看起来正确。[ 1 ]
  • 位置+深度和法线图像使用VkSampler和VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE。
  • SSAO图像为VK_FORMAT_R8_UNORM,并且正在由着色器正确写入。[ 1 ]
  • SSAO噪声图像使用VK_FORMAT_R32G32B32A32_SFLOAT格式,并且在RenderDoc中看起来正确。[ 1 ]
  • SSAO噪声图像使用带有VK_SAMPLER_ADDRESS_MODE_REPEAT的VkSampler。

SSAO噪音

// Random Generator
std::default_random_engine rndEngine(static_cast<unsigned>(glfwGetTime()));
std::uniform_real_distribution<float> rndDist(0.0f, 1.0f);

// SSAO random noise
std::vector<glm::vec4> ssaoNoise(SSAO_NOISE_DIM * SSAO_NOISE_DIM);
for (uint32_t i = 0; i < static_cast<uint32_t>(ssaoNoise.size()); i++)
{
    ssaoNoise[i] = glm::vec4(rndDist(rndEngine) * 2.0f - 1.0f, rndDist(rndEngine) * 2.0f - 1.0f, 0.0f, 0.0f);
}

SSAO内核

// Function for SSAOKernel generation
float lerp(float a, float b, float f)
{
    return a + f * (b - a);
}

// SSAO sample kernel
std::vector<glm::vec4> ssaoKernel(SSAO_KERNEL_SIZE);
for (uint32_t i = 0; i < SSAO_KERNEL_SIZE; i++)
{
    glm::vec3 sample(rndDist(rndEngine) * 2.0 - 1.0, rndDist(rndEngine) * 2.0 - 1.0, rndDist(rndEngine));
    sample = glm::normalize(sample);
    sample *= rndDist(rndEngine);
    float scale = float(i) / float(SSAO_KERNEL_SIZE);
    scale = lerp(0.1f, 1.0f, scale * scale);
    ssaoKernel[i] = glm::vec4(sample * scale, 0.0f);
}

SSAO内核XY值在-1.0至1.0之间,Z值在0.0至1.0之间:

ssaoKernel XYZ[0]: X: -0.0428458 Y: 0.0578492 Z: 0.0569087
ssaoKernel XYZ[1]: X: 0.0191572 Y: 0.0442375 Z: 0.00108795
ssaoKernel XYZ[2]: X: 0.00155709 Y: 0.0287552 Z: 0.024916
ssaoKernel XYZ[3]: X: -0.0169349 Y: -0.0298343 Z: 0.0272303
ssaoKernel XYZ[4]: X: 0.0469432 Y: 0.0348599 Z: 0.0573885
(...)
ssaoKernel XYZ[31]: X: -0.104106 Y: -0.434528 Z: 0.321963

GLSL着色器

模型

mat3 normalMatrix = transpose(inverse(mat3(ubo.view * ubo.model)));
outNormalViewSpace = normalMatrix * inNormal;
outPositionViewSpace = vec3(ubo.view * ubo.model * vec4(inPosition, 1.0));

模型片段

// These are identical to the camera
float near = 0.1; 
float far  = 100.0; 
  
float LinearizeDepth(float depth) 
{
    float z = depth * 2.0 - 1.0;
    return (2.0 * near * far) / (far + near - z * (far - near));    
}

(...)

outNormalViewSpace = vec4(normalize(inNormalViewSpace) * 0.5 + 0.5, 1.0);
outPositionDepth = vec4(inPositionViewSpace, LinearizeDepth(gl_FragCoord.z));

fullscreen.vert

outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f);

ssao.frag

#version 450

layout (location = 0) in vec2 inUV;

layout (constant_id = 1) const int SSAO_KERNEL_SIZE = 32;
layout (constant_id = 2) const float SSAO_RADIUS = 0.5;

layout (binding = 0) uniform sampler2D samplerPositionDepth;
layout (binding = 1) uniform sampler2D samplerNormal;
layout (binding = 2) uniform sampler2D samplerSSAONoise;

layout (binding = 3) uniform SSAOKernel
{
    vec4 samples[SSAO_KERNEL_SIZE];
} ssaoKernel;

layout( push_constant ) uniform UniformBufferObject {
    mat4 projection;
} ubo;

layout (location = 0) out float outSSAO;

void main() 
{
    //
    // SSAO Post Processing (Pre-Blur)
    //

    // Get a random vector using a noise lookup
    ivec2 texDim = textureSize(samplerPositionDepth, 0); 
    ivec2 noiseDim = textureSize(samplerSSAONoise, 0);
    const vec2 noiseUV = vec2(float(texDim.x) / float(noiseDim.x), float(texDim.y) / (noiseDim.y)) * inUV;   
    vec3 randomVec = texture(samplerSSAONoise, noiseUV).xyz * 2.0 - 1.0;

    // Get G-Buffer values
    vec3 fragPos = texture(samplerPositionDepth, inUV).rgb;
    vec3 normal = normalize(texture(samplerNormal, inUV).rgb * 2.0 - 1.0);

    // Create TBN matrix
    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(tangent, normal);
    mat3 TBN = mat3(tangent, bitangent, normal);

    // Calculate occlusion value
    float occlusion = 0.0f;
    for(int i = 0; i < SSAO_KERNEL_SIZE; i++)
    {       
        vec3 samplePos = TBN * ssaoKernel.samples[i].xyz;
        samplePos = fragPos + samplePos * SSAO_RADIUS; 
        
        // project
        vec4 offset = vec4(samplePos, 1.0f);
        offset = ubo.projection * offset; 
        offset.xyz /= offset.w; 
        offset.xyz = offset.xyz * 0.5f + 0.5f;  
        
        float sampleDepth = -texture(samplerPositionDepth, offset.xy).w;

        // Range check
        float rangeCheck = smoothstep(0.0f, 1.0f, SSAO_RADIUS / abs(fragPos.z - sampleDepth));
        occlusion += (sampleDepth >= samplePos.z ? 1.0f : 0.0f) * rangeCheck;  
    }
    occlusion = 1.0 - (occlusion / float(SSAO_KERNEL_SIZE));
    
    outSSAO = occlusion;
}

某处必须有错误的设置或不正确的计算,但是我不能完全依靠它。如果缺少相关内容,请随时请求其他代码段。

任何帮助,不胜感激,谢谢!

公理

幸得mlkn在评论指出了LinearizeDepth功能不看的权利。他是正确的,还有一个额外的不必要的“ * 2.0-1.0”步骤不属于该步骤。谢谢mlkn!:)

这是原始的,不正确的LinearizeDepth函数:

float LinearizeDepth(float depth) 
{
    float z = depth * 2.0 - 1.0;
    return (2.0 * near * far) / (far + near - z * (far - near));    
}

通过删除第一行,并将其更改为:

float LinearizeDepth(float depth) 
{
    return (2.0 * near * far) / (far + near - depth * (far - near));    
}

我的输出立即更改为此,这似乎是正确的: 正确的SSAO

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

为什么以下工作不起作用?(IEnumerable / IEnumerator)

来自分类Dev

为什么消息处理程序无法在调试模式下工作?

来自分类Dev

FragmentTransaction仅在纵向模式下工作

来自分类Dev

处理器仅在低频下工作

来自分类Dev

为什么getBoundingClientRect仅在本地工作?

来自分类Dev

routerLink 无法在角度 2 下工作

来自分类Dev

为什么仅在某些更新上需要重新启动?

来自分类Dev

为什么object .__ new__在这三种情况下工作不同

来自分类Dev

为什么这个指向函数的指针在没有警告或错误的情况下工作?

来自分类Dev

为什么Scala PartialFunction可以在不定义isDefinedAt的情况下工作?

来自分类Dev

为什么方法wait()在没有notify()的情况下工作?

来自分类Dev

为什么有些jsfiddle示例可以在没有外部URL的情况下工作

来自分类Dev

为什么 ping 在没有功能和 setuid 的情况下工作

来自分类Dev

为什么 strlen 在没有 '\0' 的情况下工作正常?

来自分类Dev

为什么 lubridate 不能在有光泽的情况下工作?

来自分类Dev

为什么精灵不能在一般背景下工作?

来自分类Dev

为什么 rax 和 rdi 在这种情况下工作相同?

来自分类Dev

为什么某些角度$ scope函数会“返回”操作?

来自分类Dev

为什么某些角度$ scope函数会“返回”操作?

来自分类Dev

计时器仅在调试模式下工作

来自分类Dev

SSIS脚本组件-仅在调试模式下工作

来自分类Dev

VM的Windows ME仅在安全模式下工作

来自分类Dev

FileWriter仅在没有GUI的情况下工作

来自分类Dev

为什么\ b在某些语言中无法正常工作?

来自分类Dev

为什么不能尝试捕获某些异常的工作?

来自分类Dev

为什么原始数据类型可以在不包含系统名称空间的情况下工作?

来自分类Dev

为什么某些工作包文件对ldd输出的某些库返回“未找到”?

来自分类Dev

为什么PHP认为大整数是浮点数(但仅在某些时候)?

来自分类Dev

C#:为什么传递null会导致Object []重载(但仅在某些情况下)?

Related 相关文章

  1. 1

    为什么以下工作不起作用?(IEnumerable / IEnumerator)

  2. 2

    为什么消息处理程序无法在调试模式下工作?

  3. 3

    FragmentTransaction仅在纵向模式下工作

  4. 4

    处理器仅在低频下工作

  5. 5

    为什么getBoundingClientRect仅在本地工作?

  6. 6

    routerLink 无法在角度 2 下工作

  7. 7

    为什么仅在某些更新上需要重新启动?

  8. 8

    为什么object .__ new__在这三种情况下工作不同

  9. 9

    为什么这个指向函数的指针在没有警告或错误的情况下工作?

  10. 10

    为什么Scala PartialFunction可以在不定义isDefinedAt的情况下工作?

  11. 11

    为什么方法wait()在没有notify()的情况下工作?

  12. 12

    为什么有些jsfiddle示例可以在没有外部URL的情况下工作

  13. 13

    为什么 ping 在没有功能和 setuid 的情况下工作

  14. 14

    为什么 strlen 在没有 '\0' 的情况下工作正常?

  15. 15

    为什么 lubridate 不能在有光泽的情况下工作?

  16. 16

    为什么精灵不能在一般背景下工作?

  17. 17

    为什么 rax 和 rdi 在这种情况下工作相同?

  18. 18

    为什么某些角度$ scope函数会“返回”操作?

  19. 19

    为什么某些角度$ scope函数会“返回”操作?

  20. 20

    计时器仅在调试模式下工作

  21. 21

    SSIS脚本组件-仅在调试模式下工作

  22. 22

    VM的Windows ME仅在安全模式下工作

  23. 23

    FileWriter仅在没有GUI的情况下工作

  24. 24

    为什么\ b在某些语言中无法正常工作?

  25. 25

    为什么不能尝试捕获某些异常的工作?

  26. 26

    为什么原始数据类型可以在不包含系统名称空间的情况下工作?

  27. 27

    为什么某些工作包文件对ldd输出的某些库返回“未找到”?

  28. 28

    为什么PHP认为大整数是浮点数(但仅在某些时候)?

  29. 29

    C#:为什么传递null会导致Object []重载(但仅在某些情况下)?

热门标签

归档