为什么OpenGL投影矩阵中的符号很重要?

14
我正在解决一个计算机视觉问题,需要使用已校准的相机渲染三维模型。我正在编写一个函数,将校准相机矩阵分解成模型视图矩阵和投影矩阵,但是我在OpenGL中遇到了一个有趣的现象,这种现象无法被我解释清楚。
简要描述是:取反投影矩阵会导致无法呈现任何东西(至少在我的经验中是这样)。 我本来以为通过任何标量乘以投影矩阵都不会产生影响,因为它转换齐次坐标,并且齐次坐标不受缩放影响。
以下是我认为这个现象出乎意料的原因;也许有人能指出我的推理有哪些错误。
想象以下透视投影矩阵,它可以给出正确结果:
    [ a b c 0 ]
P = [ 0 d e 0 ]
    [ 0 0 f g ]
    [ 0 0 h 0 ]

将这个乘以摄像机坐标可以得到齐次裁剪坐标:

[x_c]   [ a b c 0 ]   [X_e]
[y_c] = [ 0 d e 0 ] * [Y_e]
[z_c]   [ 0 0 f g ]   [Z_e]
[w_c]   [ 0 0 h 0 ]   [W_e]

最后,为了得到规范化的设备坐标,我们将x_c、y_c和z_c除以w_c:

[x_n]   [x_c/w_c]
[y_n] = [y_c/w_c]
[z_n]   [z_c/w_c]
现在,如果我们否定P,结果的剪辑坐标应该被否定,但由于它们是齐次坐标,乘以任何标量(例如-1)不应对结果的标准化设备坐标产生任何影响。 但是,在openGl中,否定P导致什么也没有渲染。 我可以将P乘以任何非负标量并获得完全相同的渲染结果,但一旦我乘以负标量,就不会渲染任何内容。 这里到底发生了什么??
谢谢!
3个回答

11

好的,简而言之,剪切测试是通过以下方式进行的:

-w_c < x_c < w_c
-w_c < y_c < w_c
-w_c < z_c < w_c

乘以负值会导致该测试失败。


2
好答案!我原以为剪裁测试应该是:-1 < x_c / w_c < 1但我猜OpenGL避免了除法运算,代价是当w_c为负数时评估不正确。 - Kyle Simek

2
我刚发现了一些有助于解答的信息:
来自《OpenGL编程指南》附录G:
避免使用负的w顶点坐标和负的q纹理坐标。OpenGL可能无法正确裁剪这些坐标,而且在渲染由这些坐标定义的图元时可能会出现插值错误。
反转投影矩阵将导致负的W裁剪坐标,显然OpenGL不喜欢这种情况。但是有人可以解释一下为什么OpenGL无法处理这种情况吗?
参考:http://glprogramming.com/red/appendixg.html

0

我能想到的原因:

  • 通过反转投影矩阵,坐标将不再位于视锥体的zNear和zFar平面之内(必须大于0)。
  • 为了创建窗口坐标,规范化设备坐标通过视口进行平移/缩放。因此,如果您对剪辑坐标使用了负标量,则规范化设备坐标(现在已反转)将视口转换为窗口坐标,这些坐标偏离窗口(如果您愿意,向左和向下)

另外,由于您提到使用相机矩阵并且已经反转了投影矩阵,我必须问一下...您从相机矩阵中应用于哪些矩阵?对投影矩阵进行操作会导致深度缓冲区中的各种问题,包括使用z(深度测试、面剔除等)。

OpenGL FAQ transformations 部分有更多细节。


将齐次坐标转换为非齐次坐标(即通过除以w_c实现)后,坐标仍然落在zNear和zFar之间,因为w_c的负号抵消了{x,y,z}_c的反向符号。所以我认为这不是问题所在。出于同样的原因,规范化设备坐标实际上也没有被倒置。我只使用投影矩阵进行内部相机参数(fovy、aspect、轴扭曲等);外部参数进入模型视图矩阵。因此,我认为这也不是问题所在。 - Kyle Simek
我不知道驱动程序的实现细节,但我猜测对于zNear和zFar来说实际上并非如此。剪裁测试可能不是范围而是深度,因此数值被测试为大于zNear(因此首先需要正数要求)。如果整个坐标系和矩阵被反转,则测试失败,正如bahbar所指出的那样。 - charstar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接