C++ 为什么SSAO只能从特定角度/距离工作?

C++ 为什么SSAO只能从特定角度/距离工作?,c++,graphics,3d,glsl,vulkan,C++,Graphics,3d,Glsl,Vulkan,我试图在将作为资源使用时复制。但我的SSAO代码仅部分覆盖了特定角度/距离的模型,当靠近对象时,也有非常强的自遮挡效果 左侧是我的渲染器,右侧是: 中:| 窗口:| 楼梯:| 编辑:在RenderDoc的正确图像上有一些奇怪的伪影。很抱歉 关于我的渲染器变量的一些注意事项: 位置+深度图像使用VK_格式_R32G32B32A32_SFLOAT格式,在RenderDoc中看起来正确。[][] 普通图像使用VK_格式_R8G8B8A8_UNORM格式,在RenderDoc中看起来正确。[] 位

我试图在将作为资源使用时复制。但我的SSAO代码仅部分覆盖了特定角度/距离的模型,当靠近对象时,也有非常强的自遮挡效果

左侧是我的渲染器,右侧是:

  • 中:|
  • 窗口:|
  • 楼梯:|
编辑:在RenderDoc的正确图像上有一些奇怪的伪影。很抱歉

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

  • 位置+深度图像使用VK_格式_R32G32B32A32_SFLOAT格式,在RenderDoc中看起来正确。[][]
  • 普通图像使用VK_格式_R8G8B8A8_UNORM格式,在RenderDoc中看起来正确。[]
  • 位置+深度和法线图像使用VG采样器和VG采样器、地址、模式、夹紧到边缘
  • SSAO图像是VK_格式_R8_UNORM,并且由着色器正确写入。[]
  • SSAO噪波图像使用VK_格式_R32G32B32A32_SFLOAT格式,在RenderDoc中看起来正确。[]
  • SSAO噪声图像使用VK_采样器_地址_模式_重复的VK采样器
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);
}
GLSL着色器 model.vert

mat3 normalMatrix = transpose(inverse(mat3(ubo.view * ubo.model)));
outNormalViewSpace = normalMatrix * inNormal;
outPositionViewSpace = vec3(ubo.view * ubo.model * vec4(inPosition, 1.0));
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f);
model.frag

// 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));
#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;
}
全屏.vert

mat3 normalMatrix = transpose(inverse(mat3(ubo.view * ubo.model)));
outNormalViewSpace = normalMatrix * inNormal;
outPositionViewSpace = vec3(ubo.view * ubo.model * vec4(inPosition, 1.0));
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f);
outUV=vec2((gl_VertexIndex=samplePos.z?1.0f:0.0f)*范围检查;
}
遮挡=1.0-(遮挡/浮动(SSAO_内核_大小));
OUTSAO=闭塞;
}
一定有错误的设置或不正确的计算,但我不能完全指出它。如果缺少相关的东西,请随意请求额外的代码片段

非常感谢您提供的任何帮助!

感谢您在评论中指出线性化深度函数看起来不正确。他是正确的,有一个额外的不必要的“*2.0-1.0”步骤不属于该步骤。谢谢您mlkn!)

这是原始的、不正确的线性化深度函数:

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));    
}
我的输出立即更改为,这似乎是正确的:

我怀疑在深度使用方面存在问题<代码>线性化深度看起来不正确。检查。我建议改为从反向投影矩阵计算线性深度。另外,在GBuffer中存储位置会浪费带宽和空间,您可以使用屏幕空间位置、GBuffer大小、反向投影矩阵和深度缓冲区的深度来计算。它看起来像
float2 xyNDC=FragCoordToNDC(位置S,附件大小);浮动4中间位置=摄影机。反转投影*浮动4(xyNDC.x,xyNDC.y,深度,1);float3 positionVS=intermediatePosition.xyz/intermediatePosition.w;
其中
positions
是屏幕空间位置,
depth
是深度样本,
positionVS
-视图空间位置。@mlkn您说得对,谢谢!发布了原始问题的答案。@EgorShkorov是的,听起来像是using重建位置的深度缓冲区效率更高,占用的内存更少。但是,我想在尝试之前让SSAO处理位置输出。我会尝试一下你的函数,谢谢提示!