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

Opengl glReadPixels()如何获得实际深度而不是标准化值?,opengl,depth-testing,Opengl,Depth Testing,我正在使用pyopengl获取深度贴图 我能够使用glReadPixels()获得标准化深度贴图。如何将标准化值还原为世界坐标中的实际深度 我尝试过使用gldeptrange(),但它总是执行一些规范化。我可以禁用标准化吗?绘制几何体时,顶点着色器应该通过视图/投影矩阵将所有内容转换为标准化设备坐标(其中每个组件都在-1和1之间)。无法避免,超出此范围的所有内容都将被剪裁(或剪裁,如果启用深度剪裁)。然后,将这些设备坐标转换为窗口坐标-X和Y坐标映射到使用glViewport指定的范围,Z映射到

我正在使用pyopengl获取深度贴图

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


我尝试过使用
gldeptrange()
,但它总是执行一些规范化。我可以禁用标准化吗?

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

无法禁用规范化,因为最终值必须在0..1范围内。但是您可以应用反向转换:首先,将深度值映射回-1..1范围(如果您没有使用
gldeptrange
,则只需将它们乘以2并减去1)。然后,您需要应用投影矩阵的逆矩阵-您可以通过计算其逆矩阵来显式实现,也可以通过查看透视矩阵的计算方式来避免矩阵运算。对于典型的矩阵,将使用

zNorm = 2 * zBuffer - 1
zView = 2 * near * far / ((far - near) * zNorm - near - far)
(请注意,zView将是负数,介于-近和-远之间,因为在OpenGL中,Z轴通常指向相机)


虽然通常情况下,您不希望只需要深度-您需要完整的3D点,因此您最好在标准化坐标中重建向量,然后应用反向投影/视图变换。

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

规范化设备空间的Z坐标映射到深度范围(),这在[0,1]中是通用的

视图空间的z坐标如何转换为标准化设备z坐标以及深度,取决于投影矩阵。
在正交投影时,Z分量由线性函数计算;在透视投影时,Z分量由有理函数计算。
请参见

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

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)


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

可能的重复请参见此处的功能
glReadDepth
,了解如何对透视投影执行此操作。它不完全是重复的,因为不清楚所述链接上的问题。谢谢。我可以通过移除
gldeptrange(0.0,1.0)
和从缓冲区获取深度后进行的重整化来解决这个问题。然后,我将相机参考[x,y,1]中的点乘以恢复的深度,直接获得3D点。