计算正常矩阵的正确方法?

时间:2014-12-22 09:41:01

标签: 3d

我正在尝试实现一个简单的光线跟踪应用程序,为了保持统一和干净,我决定使用像OpenGL这样的转换矩阵。我为场景中的每个节点都有一个模型矩阵。除了法线之外,一切似乎都很好(光线交集,模型转换)。根据我的阅读,可以使用glm::inverseTranspose(modelViewMatrix)来获得法线矩阵,该法线矩阵应保持法线垂直于面。 (所有的计算都是在世界空间中进行的,所以我的viewMatrix是一个标识,所以我将正常矩阵计算为glm::inverseTranspose(modelMatrix))但是,我得到了奇怪的结果:当我计算世界空间中的法线为{{1我在newNormal = normalMatrix * glm::vec4(normnal, 0.0f)坐标中得到了垃圾。我做错了什么?

1 个答案:

答案 0 :(得分:3)

从查看源代码(文档不是很有用),我对mat4x4 glm::inverseTranspose()版本的解释是它计算完整4x4矩阵的逆转置。这在某种程度上是合乎逻辑的,但实际上并不是你需要获得正常的转换矩阵。有可能从中得出所需的结果,但似乎比必要的要复杂得多。

使用4x4矩阵表示3D空间中的线性/仿射变换时,您只关心左上角的3x3部分和另外3个表示平移的元素。由于我们在法线的情况下转换向量,因此翻译部分不适用。您可以通过简单地删除其余元素将原始4x4矩阵缩减为3x3矩阵。

然后,在3x3矩阵上使用glm::inverseTranspose()可以准确地为您提供所需内容。它还可以避免使用添加0.0f作为向量的第4个元素的有点尴尬的技巧。

我没有使用过GLM,但基于doc / headers,计算应如下所示:

mat3x3 modelMatrix3(modelViewMatrix);
mat3x3 normalMatrix = = glm::inverseTranspose(modelMatrix3);
newNormal = normalMatrix * normal;

如果您不想依赖库,那么自己计算普通矩阵实际上非常简单。如果原始矩阵被写为由行向量组成:

    [ r0 ]
M = [ r1 ]
    [ r2 ]

可以通过将每一行替换为另外两行的叉积来计算正常矩阵:

    [ r1 x r2 ]
N = [ r2 x r0 ]
    [ r0 x r1 ]

这忽略了一个常数因子(1除以行列式)。但通常你不得不重新规范变换后的法线,所以常数因素并不重要。

还有一个考虑因素:在大多数现实世界的用例中,您不必担心计算特定的正常转换。大多数情况下,您可以简单地使用原始矩阵的3x3部分。只要仅根据旋转,平移和它们的组合构建转换,这就是有效的。在这种情况下,3x3部分只是旋转,矩阵的逆转置就是矩阵本身。

这也与上面的上一次计算一致,因为如果矩阵是正交的,则两个行向量的叉积总是等于第3行,这对于旋转矩阵是正确的。

均匀缩放也不会增加太多困难,因为它只是将结果向量乘以常数。只要您将结果标准化,均匀缩放就无所谓了。

合理常见的转换确实需要使用专门计算的正常矩阵来确保正确性,包括:

  • 非均匀缩放。这是一种转换类型,例如可以将球体变为椭圆体。
  • 剪切变换。不太常见,但它们确实出现在例如使用非正交坐标系的科学应用中(例如结晶学)。