3

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

我可以使用glReadPixels(). 如何将标准化值恢复为世界坐标中的实际深度?

我试过玩glDepthRange(),但它总是执行一些规范化。我可以完全禁用标准化吗?

4

2 回答 2

4

当您绘制几何图形时,您的顶点着色器应该通过视图/投影矩阵将所有内容转换为标准化的设备坐标(其中每个组件都在 -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 点,因此您不妨在归一化坐标中重建矢量,然后应用逆投影/视图变换。

于 2018-07-13T11:44:30.373 回答
1

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

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

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

这意味着,要将深度缓冲区的深度转换为原始 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)

请注意,在任何情况下,逆投影矩阵的变换都会将归一化的设备坐标变换为视图空间中的坐标。

于 2018-07-15T15:12:31.873 回答