从对数深度缓冲区获取世界空间位置

10

在将我的延迟渲染器改为使用对数深度缓冲后,我无论如何都解决不了如何从深度缓冲区的值中重建世界空间深度。

当我使用OpenGL默认的z/w深度时,我可以通过从窗口空间转换为NDC空间,然后执行逆透视变换轻松计算出这个值。

我在第二次渲染片段着色器中完成了所有这些操作:

uniform sampler2D depth_tex;

uniform mat4 inv_view_proj_mat;

in vec2 uv_f;

vec3 reconstruct_pos(){
    float z = texture(depth_tex, uv_f).r;
    vec4 pos = vec4(uv_f, z, 1.0) * 2.0 - 1.0;
    pos = inv_view_proj_mat * pos;

    return pos.xyz / pos.w;
}

然后得到了一个看起来相当正确的结果:

Cube rendered with correct world-space position reconstruction

但是现在,获得简单的z值并不那么容易(似乎也不应该很难)。

我第一个使用对数深度的顶点着色器:

#version 330 core
#extension GL_ARB_shading_language_420pack : require

layout(location = 0) in vec3 pos;
layout(location = 1) in vec2 uv;

uniform mat4 mvp_mat;

uniform float FC;

out vec2 uv_f;
out float logz_f;
out float FC_2_f;

void main(){
    gl_Position = mvp_mat * vec4(pos, 1.0);

    logz_f = 1.0 + gl_Position.w;

    gl_Position.z = (log2(max(1e-6, logz_f)) * FC - 1.0) * gl_Position.w;

    FC_2_f = FC * 0.5;
}

我的片段着色器:

#version 330 core
#extension GL_ARB_shading_language_420pack : require

// other uniforms and output variables

in vec2 uv_f;
in float FC_2_f;

void main(){
    gl_FragDepth = log2(logz_f) * FC_2_f;
}

我尝试了几种不同的方法来正确获取z轴位置,但都失败了。

如果我在第二次遍历中重新定义我的reconstruct_pos如下:

vec3 reconstruct_pos(){
    vec4 pos = vec4(uv_f, get_depth(), 1.0) * 2.0 - 1.0;
    pos = inv_view_proj_mat * pos;

    return pos.xyz / pos.w;
}

这是我目前重建 Z 的尝试:

uniform float FC;

float get_depth(){
    float log2logz_FC_2 = texture(depth_tex, uv_f).r;
    float logz = pow(2, log2logz_FC_2 / (FC * 0.5));
    float pos_z = log2(max(1e-6, logz)) * FC - 1.0; // pos.z
    return pos_z;
}

解释:

log2logz_FC_2:写入深度缓冲区的值,因此是log2(1.0 + gl_Position.w) * (FC / 2)

logz:简单地是1.0 + gl_Position.w

pos_z:透视除法之前的gl_Position.z

返回值:gl_Position.z

当然,这只是我的工作方式。我不确定这些值最终实际上代表什么,因为我认为我在某些数学方面搞砸了,或者没有正确理解正在进行的转换。

从这个对数深度缓冲区中获取世界空间Z位置的正确方法是什么?

1个回答

5

最终我整个思路都错了。使用日志缓冲区获取世界空间位置的方法是:

  1. 从纹理中获取深度
  2. 重构gl_Position.w
  3. 线性化重构的深度
  4. 转换到世界空间

这是我的GLSL实现:

in vec2 uv_f;

uniform float nearz;
uniform float farz;

uniform mat4 inv_view_proj_mat;

float linearize_depth(in float depth){
    float a = farz / (farz - nearz);
    float b = farz * nearz / (nearz - farz);
    return a + b / depth;
}

float reconstruct_depth(){
    float depth = texture(depth_tex, uv_f).r;
    return pow(2.0, depth * log2(farz + 1.0)) - 1.0;
}

vec3 reconstruct_world_pos(){
    vec4 wpos =
        inv_view_proj_mat *
        (vec4(uv_f, linearize_depth(reconstruct_depth()), 1.0) * 2.0 - 1.0);

    return wpos.xyz / wpos.w;
}

我现在使用的方式可以得到和默认OpenGL深度缓冲区相同的结果(但精度更高)。


是的,我使用了您的解决方案,得到了正确的结果。非常好! - learner
你的回答在我搜寻网页数小时后拯救了我,你是我的救命恩人。 - N8Javascript

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