首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >环境遮挡深度缓冲区归一化

环境遮挡深度缓冲区归一化
EN

Computer Graphics用户
提问于 2021-11-24 08:29:39
回答 2查看 195关注 0票数 0

从屏幕空间环境遮挡方法的比较研究

http://frederikaalund.com/wp-content/uploads/2013/05/A-Comparative-Study-of-Screen-Space-Ambient-Occlusion-Methods.pdf

(附录中阴影)特别是炼金术AO的实施。我得到了一些奇怪的结果,似乎找不出是什么原因造成的。我觉得它可以使用深度缓冲区,因为我不确定"ndc_linear_depth“是什么意思。我被告知要实施一个

正规化装置坐标线性深度

这让我感到困惑,因为Vulkan深度已经是0-1,NDC是-1:1,这会导致深度不准确?无论如何,我实现了我认为是正确的,但这似乎没有为炼金术AO产生正确的结果。我很想知道是否是深度缓冲区导致了这个问题。会很感激你的洞见。我用的是Vulkan和GLSL

我的阴影:

代码语言:javascript
复制
layout(binding = 3) uniform sampler2D texNoise;
layout(binding = 6) uniform sampler2D depthMap;
layout(binding = 2) uniform sampler2D gNormal;

layout(location = 1) in vec2 uvCoords;

layout(binding = 5) uniform UniformBufferObject {
    mat4 model;
    mat4 view;
    mat4 proj;
    float time;
}camera;

layout(binding = 4) uniform KernelSample {
    vec3 samples[64];
    mat4 projection;
    vec4 camera_eye;
    vec4 camera_center;
    float z_far;
}kernelsamples;

layout(location = 0) out vec4 outColor;

vec4 ambient_occlusion;

const vec2 window = vec2(1920.0, 1080.0);

float near = 0.1;
float zfar = 1000;

float tc_depth(in vec2 tc)
{
    return texture(depthMap, vec2(1.0 - uvCoords.x, uvCoords.y)).x;
}

float LinearizeDepth(in vec2 tc) {
    float buffer_z = texture(depthMap, tc).x;
    return (2.0 * near) / (zfar + near - buffer_z * (near - zfar));
}

float ndcDepth(in vec2 tc) {
    float depth = texture(depthMap, tc).x;

    return camera.proj[3][2] / (camera.proj[2][2] + depth);
}

float ec_depth(in vec2 tc)
{
    float buffer_z = texture(depthMap, tc).x;
    return camera.proj[3][2] / (-2.0 * buffer_z + 1.0 - camera.proj[2][2]);
}

void main() 
{
    vec2 tc_depths = gl_FragCoord.xy / window;
    
    // Normals
    vec3 normals = normalize(texture(gNormal, tc_depths).rgb * 2.0 - 1.0);

    // normals in world coordinates
    vec3 wc_normal = normalize(transpose(mat3(camera.view)) * normals);

    float ndc_linear_depth = ndcDepth(tc_depths);

    // camera_center(target) - camera_eye = world coordinate camera ray direction
    vec3 wc_position_current_frag = kernelsamples.camera_eye.xyz + normalize((kernelsamples.camera_center.xyz - kernelsamples.camera_eye.xyz)) * ndc_linear_depth;

    ambient_occlusion.a = 0.0; 

    const int base_samples = 0;
    const int min_samples = 64;
    const float radius = 10.0;
    const float projection_factor = 0.75;
    const float bias = 1.000;
    const float sigma = 2.0;
    const float epsilon = 0.0001;

    int samples = max(int(base_samples / (1.0 + base_samples * ndc_linear_depth)), min_samples);

    mat4 inverse_view_projection_matrix = inverse(camera.view);
    float projected_radius = radius * projection_factor / ndcDepth(tc_depths);

    vec2 inverted_random_texture_size = 1.0 / vec2(textureSize(texNoise, 0));
    vec2 tc_random_texture = gl_FragCoord.xy * inverted_random_texture_size;

    // scale texture back to [-1: 1] range
    vec3 random_direction = texture(texNoise, tc_random_texture).xyz;
    random_direction = normalize(random_direction * 2.0 - 1.0);

    for(int i = 0; i < samples; i++) 
    {
        // select random point
        vec2 sample_random_direction = texture(texNoise,  vec2(float(i) * inverted_random_texture_size.x, float(i / textureSize(texNoise, 0).x) * inverted_random_texture_size.y)).xy;
        sample_random_direction = sample_random_direction * 2.0 - 1.0; //to screen coordinates

        //read position and depth for sample
        vec3 tc_sample;
        tc_sample.xy = tc_depths + sample_random_direction * projected_radius;
        tc_sample.z = tc_depth(tc_sample.xy);

        vec3 ndc_sample = tc_sample * 2.0 - 1.0;
        vec4 temporary = inverse_view_projection_matrix * vec4(ndc_sample, 1.0);
        vec3 wc_sample = temporary.xyz / temporary.w;

        vec3 v = wc_sample - wc_position_current_frag;

        ambient_occlusion.a += max(0.0, dot(v, wc_normal) - bias) / (dot(v, v) + epsilon);
    }

    ambient_occlusion.a = max(0.0, 1.0 - 2.0 * sigma / float(samples) * ambient_occlusion.a);

    outColor = vec4(ambient_occlusion.a, ambient_occlusion.a, ambient_occlusion.a, 1.0); 

}

结果来自于我的着色器

原始着色器(也在附录中)

EN

回答 2

Computer Graphics用户

回答已采纳

发布于 2021-12-02 12:39:36

这里是一个线性化的深度函数,期望非线性值在0,1和工作在Vulkan之间。

代码语言:javascript
复制
float LinearizeDepth(float d)
{
   return zNear * zFar / (zFar + d * (zNear - zFar));
}

我建议将纹理查找和线性化分解为单独的函数,以帮助保持代码库的干净/可读性,并减少bug。

还请注意,如果这不起作用,那么代码的问题就会运行得更深,然后只是深度线性化。

票数 1
EN

Computer Graphics用户

发布于 2021-11-26 09:04:37

我觉得它可以使用深度缓冲区,因为我不确定"ndc_linear_depth“是什么意思。我被告知要实现一个标准化的设备坐标线性深度,这使我感到困惑,因为Vulkan深度已经是0-1,而NDC是-1:1,这会导致深度不精确吗?

NDC (归一化设备坐标)只是一个坐标系统,其所有轴的取值范围为-1到1。如果您的值在0到1之间,那么简单的转换将是:

2 \cdot \mathbf{v} - 1

其中\mathbf{v}是您的坐标向量或深度值。它基本上“只是两倍的距离”之间的深度值,但关系保持不变。因此,如果一个像素距离摄像机平面的距离是原来坐标系中的另一个像素的4倍,那么在转换之后,它仍然会更近4倍。由于我们在这里使用浮点数,你应该得到的唯一不准确之处是通常的浮点数学错误(很可能可以忽略不计)。但是,如果您只将值存储在0到1之间的缓冲区中,该缓冲区支持从-1到1之间的值,则只使用其值范围的一半,并且有效地降低了50%的可用精度。

现在让我们来讨论“线性”部分。在线性深度缓冲区中,两个深度值之间的差值总是转换为相同的距离。因此,如果您的NDC深度缓冲区覆盖了10米的区域,则-0.3-0.1值之间的差异将是1米,就像对于值0.81.0一样。

现在考虑一下,深度缓冲区有一组有限的值,由可用位数决定。用于渲染目的的线性深度缓冲区的问题是,您在遥远的对象上“浪费比特”。您无法真正判断1公里之外的对象是否比另一个对象更接近1厘米。如果两个物体都离我们一米远,那就更明显了。物体越近,距离差就越小。但是,在某种程度上,您达到了深度缓冲区的最小可表示距离差。这会导致不想要的效果,就像一个更远的对象被呈现在一个更近的对象前面,仅仅因为它们共享相同的深度缓冲区值,这是由于有限的值。

为了解决这个问题,人们可以将非线性函数应用到深度值上,这样你就可以区分更近的物体的距离,而不是远距离的物体。在这个非线性深度缓冲区中,假设你的相机位于深度值-1,那么-0.3-0.1值之间的覆盖距离可能只有几米,而值0.81.0之间的距离现在超过了一公里。对此的更好解释可以找到这里(请参阅深度值精度一节)

因此,回到您的问题:如果在期望线性NDC值的方程中使用非NDC值、非线性值或两者,则很可能得到错误的结果。这可能是导致你的“奇怪结果”的原因。

票数 1
EN
页面原文内容由Computer Graphics提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://computergraphics.stackexchange.com/questions/12365

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档