我正在尝试实现一个简单的光线跟踪应用程序,为了保持统一和干净,我决定使用像OpenGL这样的转换矩阵。我为场景中的每个节点都有一个模型矩阵。除了法线之外,一切似乎都很好(光线交集,模型转换)。根据我的阅读,可以使用glm::inverseTranspose(modelViewMatrix)
来获得法线矩阵,该法线矩阵应保持法线垂直于面。 (所有的计算都是在世界空间中进行的,所以我的viewMatrix是一个标识,所以我将正常矩阵计算为glm::inverseTranspose(modelMatrix)
)但是,我得到了奇怪的结果:当我计算世界空间中的法线为{{1我在newNormal = normalMatrix * glm::vec4(normnal, 0.0f)
坐标中得到了垃圾。我做错了什么?
答案 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行,这对于旋转矩阵是正确的。
均匀缩放也不会增加太多困难,因为它只是将结果向量乘以常数。只要您将结果标准化,均匀缩放就无所谓了。
合理常见的转换确实需要使用专门计算的正常矩阵来确保正确性,包括: