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

时间:2018-07-12 23:40:38

标签: opengl depth-testing

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

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

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

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

答案 1 :(得分:1)

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

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

视图空间的z坐标如何转换为规格化的设备Z坐标以及深度,取决于投影矩阵。
在正交投影中,Z分量由线性函数计算,而在透视投影中,Z分量由有理函数计算。
参见 How to render depth linearly in modern OpenGL with gl_FragCoord.z in fragment shader?

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

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

正交投影

n = near, f = far

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

z_linear = z_eye

透视投影

n = near, f = far

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

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

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

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