OpenGL 深度缓冲区的范围很短

OpenGL Depth Buffer has short range

和"OpenGL Depth Buffer has short range"我不是说远平面太近了,是深度缓冲作为贴图的问题。如果我查看缓冲区,它只会显示非常接近的对象。不知道怎么解释比较好,看图就好了。

在右上角可以看到深度缓冲。 图片可以在这里找到:http://imgur.com/a/wL87b

如您所见,我必须靠得很近才能看到深度缓冲区中的一些黑暗。

下面是为 FBO 创建深度缓冲区的代码:

对于深度纹理:

depthTexture = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, depthTexture);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL14.GL_DEPTH_COMPONENT24, width, height, 0, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, (ByteBuffer) null);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL11.GL_TEXTURE_2D, depthTexture, 0);

对于深度缓冲区:

depthBuffer = GL30.glGenRenderbuffers();
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthBuffer);
GL30.glRenderbufferStorageMultisample(GL30.GL_RENDERBUFFER, multisampleing, GL14.GL_DEPTH_COMPONENT24, width, height);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthBuffer);

您可以忽略 GLXX。在 OpenGL 方法的开头。

如果您需要更多代码,请告诉我。

As you can see I have to get very close to see some darkness in the depth buffer.

这就是双曲线深度缓冲区的工作原理。

让我们看一下投影矩阵(我使用的是经典的 OpenGL 约定,其中相机沿 -z 观察眼睛 space,投影矩阵从右手翻转到左手 space):

.    0       .             0
0    .       .             0
0    0  -(f+n)/(f-n)  -2*f*n/(f-n)
0    0      -1             0

(. 只是一些我们在这里不需要关心的数字)。

当你将该矩阵与一些眼睛 space 向量 (x_eye, y_eye, z_eye, 1) 相乘时,你最终会得到

x_clip = ...
y_clip = ....
z_clip =  [-(f+n)/(f-n) ] *z_eye + [-2*f*n/(f-n)] * 1
w_clip = -z_eye

透视除以 w_clip 后,我们最终得到标准化设备坐标 (ndc) 中的 z 值:

z_ndc = z_clip / w_clip = (f+n)/(f-n) + 2*f*n/[(f-n)*z_eye]

最后,应用 glDepthRange 达到 window space z。默认值是从 [-1,1] 到 [0,1],所以让我们在这里这样做:

z_win = 0.5 * z_ndc + 0.5 = 0.5*(f+n)/(f-n)  + f*n/[(f-n)*z_eye] + 0.5

这显然是 z_eye 的函数,也是 fn 的函数。假设您使用的是距离为 1 的近平面和距离为 1001 的远平面,因此计算结果为:

z_win(z_eye) = 1002/2000 + 1001/(1000 *z_eye) + 0.5 = 1001/1000 + 1001/(1000 * z_eye)

那么,让我们检查一下到目前为止我们得到了什么:

z_win(-1) = 0.  a point on the near plane ends up as 0 in the depth buffer
z_win(-1001) = 1.  a point on the far plane ends up as 1 in the depth buffer

这不应该让我们感到惊讶,因为这是每个构造。但是中间的点会发生什么:

z_win(-50)  = 1001/1000 - 1001/50000  = 0.98098
z_win(-100) = 1001/1000 - 1001/100000 = 0.99099
z_win(-250) = 1001/1000 - 1001/250000 = 0.996996
z_win(-500) = 1001/1000 - 1001/500000 = 0.998998
z_win(-750) = 1001/1000 - 1001/750000 = 0.999665333

因此,如您所见,任何在眼睛 space 中距离超过 100 个单位的物体最终都会有一个 > 0.99 的深度缓冲值。

换句话说,我们可以只计算一个点的眼睛 space z,它将在深度缓冲区中获得 0.5:

z_eye(z_win) = 1001/(1000*z_win -1001)
z_eye(0.5) = -1.99800399

对,没错,1到1001个单位远的截头体,只有摄像头前面1到2个单位的范围会映射到深度缓冲范围的前半部分,998个单位之后塞进下半场

因此,如果您尝试将深度缓冲区可视化为颜色,除了最接近的部分,您什么也看不到。使用 8 位颜色,高于 254/255 = .996 的所有内容都将完全饱和(在我的示例中约为 200 个单位),甚至低于该值,差异将非常小,几乎看不到。

如果你只想可视化深度缓冲区,你应该反转双曲线失真,并可视化线性(=eye space)深度。