正确转换表面法线

6
根据OpenGL红皮书的附录F,一个常规的3D变换矩阵M可以用于计算对法向量的作用,如下所示:
normalTransformed = transpose(inverse(M)) * normal

然而,虽然与转换后法线相关联的正交平面确实平行于转换后的表面,但转换后的法线向量本身可能指向我所期望的相反方向,即'进入'表面而不是'从表面出去'。

如果我希望normalTransformed指向正确的方向(即当其附加到的表面未转换时指向的方向相同),我应该如何在数学上实现?

示例

假设我的表面法线为(0,0,1),我的变换是Z方向上的平移10。变换矩阵M如下:

1 0 0 0
0 1 0 0
0 0 1 10
0 0 0 1

转置(逆M)然后是:

1 0 0 0
0 1 0 0
0 0 1 0
0 0 -10 1

应用于表面法线(0,0,1),即在齐次坐标中表示为(0,0,1,1),得到:

normalTransformed = (0,0,1,-9)

回到非齐次坐标:

(0,0,-1/9)

归一化为长度1:

(0,0,-1)

与原始法线向量(0,0,1)相比,该向量指向相反的方向。


2
这对我来说不太有意义。为什么正常情况会指向错误的方向?你能举个例子说明一下吗?我认为你那里的代码片段应该是正确的。 - Tim
好的,假设我的表面法线为(0, 0, 1);并且我的变换是沿着Z方向平移10个单位。那么变换矩阵就是: - reddish
好的,假设我的表面法线是(0,0,1);并且我的变换是在Z方向上平移10个单位。变换矩阵M如下:1 0 0 0 0 1 0 0 0 0 1 10 0 0 0 1其转置(逆矩阵)为:1 0 0 0 0 1 0 0 0 0 1 0 0 0 -10 1应用于表面法线(0,0,1),即齐次坐标中的(0,0,1,1),得到:normalTransformed = (0, 0, 1, -9)回到非齐次坐标: (0, 0, -1/9)归一化为长度为1:(0, 0, -1)与原始法线向量相比,指向相反的方向。 - reddish
很抱歉,这样的回答没有帮到你,我不知道在注释中添加格式是困难的(我在这里还比较新)。我会尝试将示例放在原始问题中。(完成) - reddish
4个回答

19
应用于表面法向量(0,0,1),即在齐次坐标中为(0,0,1,1)。
好的,请停下来。
如果您想将表面法线视为齐次坐标,则W分量应使用零而不是1。您可能很快就会意识到不能除以零,但这也是为什么不对法向量进行齐次数学运算的原因。
法向量不是位置;它是一个方向。方向没有位置,因此对其进行平移毫无意义。W=0的齐次位置表示与“位置”无限远(这就是为什么您不能将它们相除的原因)。无限远的位置与每个有限点都无限远。
因此,无穷远处的位置是一个方向:无论从哪个(有限的)位置看它,它的方向都不会改变。
现在,如果您有一个4x4矩阵并需要通过它转换一个法向量,则只使用W=0是因为它使数学计算正确。它消除了矩阵的平移分量。应完全忽略后转换的W分量。
因此,在转换后,您会得到:
法线变换后 = (0,0,1,-9)
在忽略W分量后变为:
法线变换后 = (0,0,1)
更有可能的是,您的法线实际上并没有指向正确的方向。当然,在没有代码和数据的情况下,很少有更多可以说的,但数学运算假定输入合法。
此外,请不要在着色器中进行逆/转置操作。在CPU上进行操作并将结果矩阵传递到着色器中。

我并不是在进行实际的图形编程,而是在利用3D变换来做其他事情。 - reddish
我在问题中添加了一个示例来说明我的问题。 - reddish
我可以看出,将 w=0 的法线作为标准是有意义的。在我的示例中,使用齐次坐标,这会使得变换后的法线为 (0, 0, 1, -10)。我能理解为什么 w 坐标的大小不包含任何信息,但我不太明白为什么其符号也是如此。无疑,(0, 0, 1, +10) 和 (0, 0, 1, -10) 之间存在差异,对吗?您的建议,即完全忽略变换后的 W 坐标,可能是正确的,但从数学上讲,这似乎不太合理。有什么理由可以支持这种做法? - reddish
你的示例变换是一种翻译。正如尼古拉所说:“法向量不是位置,它是方向。方向没有位置,因此将其翻译是没有意义的。” - Bart
@Bart:翻译应该是一个恒等操作;它不是“无意义”的。我现在最好奇的是舍弃齐次变换法线的“w”坐标的数学理论依据。直觉上,我感觉至少“w”坐标的符号是不能被简单忽略的——我看不出转换后的法向量(0, 0, 1, +10)和(0, 0, 1, -10)应该被视为相同。 - reddish
2
@reddish:因为它是一个“方向”。只有当W=0时,齐次方向才有效。如果W是其他任何值,它就不再是一个方向;它是一个位置。 - Nicol Bolas

2
问题在于你正在除以 w 坐标,就好像你的法线是一个点一样。(当 w<0 时,这个除法会导致你的法线反转。) 相反,你需要完全忽略 w -坐标:放弃它,而不是除以它。
你的法线不是一个点,技术上来说它是一个协变量(这就是为什么它与点和向量的转换方式不同)。它实际上没有 w -坐标——添加一个的唯一原因是为了使用现有的 4x4 矩阵例程的方便。
如果你确实添加了任意的 w -坐标,你就有了具有给定法线的平面的齐次坐标。像法线一样,这样的平面通过所用于转换点的逆转置矩阵进行变换。(请注意,将平面除以其 w -坐标是没有意义的——平面也不是一个点!)
如果法线是从三角形派生出来的,那么三角形的平面应该有那个法线——但是,法线明显缺少确定确切平面的 w -坐标。给法线添加任意的 w(无论是 01 还是其他什么)意味着选择一个具有该法线的任意平面,因此将其进行变换会产生一个具有变换后法线的任意平面;这就是为什么你需要在使用 4x4 矩阵进行变换后忽略 w

1
如果您应用的仿射变换颠倒了坐标系的“左右性”,则应该仅在法线的相对方向上进行反转。例如,如果您按比例缩放[1, 1, -1],就会发生这种情况。
根据书籍《基于物理的渲染》的说法,您可以通过计算左上角3x3(法线?)矩阵的行列式来检查此情况。如果行列式为负,则矩阵将改变左右性,并且您应该反转法线。
[我昨天刚刚读到这个,这是我从记忆中引用的]。

1

尼古拉斯·波拉斯在他的回答中所写的是绝对正确的,我不会重复那些概念。

但我注意到问题中有一些有趣的点。

首先,正常矩阵通常被定义为模型视图矩阵左上角3x3矩阵的逆转置。这是因为法线不使用齐次位置定义,实际上不需要4x4矩阵;如果您想使用整个模型视图矩阵,请遵循尼古拉斯·波拉斯的指导,但数学仍然相同(因为w为零)。

其次,您已经说明:

我希望normalTransformed指向正确的方向(即当它所附着的表面未经变换时指向的方向相同),从数学上讲应该如何做到这一点?

正常矩阵用于与模型变换一致地转换法线(事实上,正常矩阵是从模型视图矩阵派生出来的)。从您的引用中我可以理解到,您希望法线不被转换...实际上,为什么要转换它?您可以直接使用“normal”。


我想我没有完全清楚 - 我确实希望得到转换后的法线; 但是我需要它与未转换和转换版本所属表面之间具有相同的关系,也就是说:如果法线指向未转换的表面外部,则对于转换版本也应该如此; 而我认为我看到的是,在某些情况下,转换后的版本指向“进入”表面,即与所需的转换后法线向量相反180度。 - reddish
现在我明白了。确实需要更多的细节:你的输入、代码以及在“这些情况下”的结果。否则你就会独自一人,因为理论已经被利用了。 - Luca

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