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
的函数,也是 f
和 n
的函数。假设您使用的是距离为 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)深度。
和"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
的函数,也是 f
和 n
的函数。假设您使用的是距离为 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)深度。