关于opengl:glReadPixels()如何获取实际深度而不是标准化值?

glReadPixels() how to get actual depth instead of normalized values?

我正在使用pyopengl来获取深度图。

我能够使用glReadPixels()获得归一化的深度图。如何将归一化的值还原为世界坐标中的实际深度?

我尝试过使用glDepthRange(),但是它总是执行一些标准化操作。我可以完全禁用标准化吗?


绘制几何图形时,应该使用顶点着色器通过视图/投影矩阵将所有内容转换为归一化的设备坐标(每个分量在-1和1之间)。无法避免这种情况,超出此范围的所有内容都将被裁剪(或启用深度钳位,则被钳位)。然后,将这些设备坐标转换为窗口坐标-将X和Y坐标映射到用glViewport指定的范围,将Z映射到用glDepthRange设置的范围。

您不能禁用标准化,因为最终值必须在0..1范围内。但是您可以应用逆变换:首先,将深度值映射回-1..1范围(如果您不使用glDepthRange,您要做的就是将它们乘以2并减去1)。然后,您需要应用投影矩阵的逆矩阵-您可以通过计算其逆矩阵来明确地进行此操作,也可以通过查看透视矩阵的计算方式来避免矩阵运算。对于典型矩阵,逆变换将为

1
2
zNorm = 2 * zBuffer - 1
zView = 2 * near * far / ((far - near) * zNorm - near - far)

(请注意,zView在-near和-far之间为负,因为在OpenGL中,Z轴通常指向相机)。

尽管通常您不只想要深度-想要完整的3D点,所以您也可以在归一化坐标中重建矢量,然后应用逆投影/视图变换。


投影到视口后,场景的坐标为归一化设备坐标(NDC)。规范化的设备空间是一个立方体,其左,下,前坐标为(-1,-1,-1),右,上,后坐标为(1、1、1)。此多维数据集中的几何图形在视口上是"可见的"(除非被覆盖)。

归一化设备空间的Z坐标映射到通常在[0,1]中的深度范围(glDepthRange)。

视图空间的z坐标如何转换为规格化的设备Z坐标以及深度,取决于投影矩阵。
在正交投影中,Z分量由线性函数计算,在透视投影中,Z分量由有理函数计算。
请参见如何在片段着色器中使用gl_FragCoord.z在现代OpenGL中线性渲染深度?。

这意味着要将深度缓冲区的深度转换为原始Z坐标,投影(正交或透视),并且必须知道近平面和远平面。

以下假定深度范围为[0,1],并且depth是该范围内的值:

正射投影

1
2
3
4
5
n = near, f = far

z_eye = depth * (f-n) + n;

z_linear = z_eye

透视投影

1
2
3
4
n = near, f = far

z_ndc = 2 * depth - 1.0;
z_eye = 2 * n * f / (f + n - z_ndc * (f - n));

如果已知透视投影矩阵,则可以执行以下操作:

1
2
3
A = prj_mat[2][2]
B = prj_mat[3][2]
z_eye = B / (A + z_ndc)

请注意,在任何情况下,通过逆投影矩阵进行转换,都可以将规范化的设备坐标转换为视图空间中的坐标。