我正在尝试实现一个简单的光线跟踪应用程序,并且为了使事情保持一致和整洁,我决定使用像OpenGL这样的转换矩阵。我对场景中的每个节点都有一个模型矩阵,除了法线之外,其他所有东西似乎都可以正常工作(光线相交,模型转换)。根据我的阅读,可以glm::inverseTranspose(modelViewMatrix)用来获取法线矩阵,该法线矩阵应保持法线垂直于面部。(所有的计算都是在世界空间中进行的,因此我的viewMatrix是一个恒等式,因此我将法线矩阵计算为glm::inverseTranspose(modelMatrix))。但是,我得到了奇怪的结果:当我在世界空间中计算法线时newNormal = normalMatrix * glm::vec4(normnal, 0.0f),其w坐标中出现了垃圾。我究竟做错了什么?
通过查看源代码(该文档不是很有帮助),我对mat4x4版本的解释glm::inverseTranspose()是它计算了完整的4x4矩阵的逆转置。这在某种程度上是合乎逻辑的,但实际上并不是获得正常转换矩阵所需要的。可能可以从中得出所需的结果,但是似乎比必需的要复杂得多。
当使用4x4矩阵表示3D空间中的线性/仿射变换时,您只需要关心左上3x3部分以及表示平移的其他3个元素。由于我们是在法线的情况下转换向量,因此平移部分不适用。您只需删除其余元素,就可以将原始的4x4矩阵缩小为3x3矩阵。
然后,glm::inverseTranspose()在3x3矩阵上使用应该可以为您提供所需的确切信息。它还将避免不得不使用比较笨拙的把法添加0.0f作为向量的第4个元素。
我没有使用GLM,但是基于doc / header,计算应如下所示:
mat3x3 modelMatrix3(modelViewMatrix);
mat3x3 normalMatrix = = glm::inverseTranspose(modelMatrix3);
newNormal = normalMatrix * normal;
Run Code Online (Sandbox Code Playgroud)
如果您不想依赖库,则自己计算法线矩阵实际上很容易。如果原始矩阵写为由行向量组成:
[ r0 ]
M = [ r1 ]
[ r2 ]
Run Code Online (Sandbox Code Playgroud)
可以通过用其他两行的叉积替换每一行来计算法线矩阵:
[ r1 x r2 ]
N = [ r2 x r0 ]
[ r0 x r1 ]
Run Code Online (Sandbox Code Playgroud)
这忽略了一个常数因子(1除以行列式)。但是通常,无论如何,无论如何您通常都必须对转换后的法线进行重新归一化,因此常数因子并不重要。
还有一个考虑因素:在大多数实际的用例中,您不必担心计算特定的正态变换。大多数时候,您可以简单地使用原始矩阵的3x3部分。只要仅从旋转,平移及其组合中构建变换,此方法就有效。在这种情况下,3x3部分只是旋转,而矩阵的逆转置就是矩阵本身。
这也与上面的最后一个计算一致,因为如果矩阵是正交的,则两个行向量的叉积始终等于第三行,这对于旋转矩阵而言是正确的。
均匀缩放也不会增加太多困难,因为它只是将结果向量乘以一个常数。只要您对结果进行标准化,统一缩放就无关紧要。
您确实确实需要使用经过特殊计算的法线矩阵以确保正确性的合理常见转换包括: