【问题标题】:Why transform normals with the transpose of the inverse of the modelview matrix?为什么用模型视图矩阵的逆转置来转换法线?
【发布时间】:2012-11-19 05:10:53
【问题描述】:

我正在处理一些着色器,我需要转换法线。

我在几个教程中读到了变换法线的方式是将它们与模型视图矩阵的逆矩阵的转置相乘。但我找不到解释为什么会这样,这背后的逻辑是什么?

【问题讨论】:

    标签: opengl glsl game-engine game-physics


    【解决方案1】:

    它源于法线的定义。

    假设您有法线N 和一个向量V,这是一个与法线在对象上相同位置的切线向量。然后根据定义N·V = 0

    切线向量与物体表面的方向相同。因此,如果您的表面是平面的,则切线是对象上两个可识别点之间的差异。因此,如果V = Q - R 其中QR 是表面上的点,那么如果您通过B 变换对象:

    V' = BQ - BR
       = B(Q - R)
       = BV
    

    通过考虑限制,同样的逻辑适用于非平面表面。

    在这种情况下,假设您打算通过矩阵B 转换模型。所以B 将应用于几何。然后要弄清楚如何处理您需要为矩阵求解的法线,A 以便:

    (AN)·(BV) = 0
    

    将其转换为行与列以消除显式点积:

    [tranpose(AN)](BV) = 0
    

    把转置拉到外面,去掉括号:

    transpose(N)*transpose(A)*B*V = 0
    

    这就是“法线的转置”[product with]“已知变换矩阵的转置”[product with]“我们正在求解的变换”[product with]“表面上的向量模型" = 0

    但我们首先声明 transpose(N)*V = 0,因为这与声明 N·V = 0 相同。所以为了满足我们的约束,我们需要表达式的中间部分——transpose(A)*B——消失。

    因此我们可以得出结论:

     transpose(A)*B = identity
     => transpose(A) = identity*inverse(B)
     => transpose(A) = inverse(B)
     => A = transpose(inverse(B))
    

    【讨论】:

    • @NicolBolas V,正如我所说,是表面上的一个向量,而不是一个位置。所以,是的,N.V 是点积。我同样可以将其表述为“沿表面的向量”。正如您所指出的,通过正式提及点积立即排除了替代解释。
    • (虽然从技术上讲,“沿表面的矢量”会不太准确,因为它假定表面是平坦的;我将进行编辑以明确提及“切线”这个词)
    • @NicolBolas 相反,“您打算通过矩阵A 转换模型”对我来说似乎很清楚。您的问题暗示了我以错误的方式口头描述了 A 和 B。
    • 还有一个问题。如果B是模型变换矩阵,我们怎么知道切线V需要乘以它而不是不是正常的N
    • @NicolBolas 好点。我的意思是,你和我都知道这是因为切线的一个定义是当 h 趋向于零时相隔 h 个单位的两点之间的差值之间的限制,但除非我在那里编辑它,否则这不是一个完整的答案。
    【解决方案2】:

    我最喜欢的证明如下,其中 N 是法线,V 是切向量。由于它们是垂直的,它们的点积为零。 M 是任何 3x3 可逆变换 (M-1 * M = I)。 N'和V'是经过M变换的向量。

    为了获得一些直觉,请考虑下面的剪切变换。

    请注意,这适用于切向量。

    【讨论】:

    • 最后一个有错别字。它应该说 $(M^-T N)^T MV = 0$ (前两个矩阵的产生周围缺少转置)。
    【解决方案3】:

    看看这个教程:

    https://paroj.github.io/gltut/Illumination/Tut09%20Normal%20Transformation.html

    您可以想象,当球体的表面拉伸(因此球体沿一个轴或类似的东西缩放)时,该表面的法线将全部“弯曲”到彼此。事实证明,您需要反转应用于法线的比例来实现这一点。这与使用逆转置矩阵进行变换相同。上面的链接显示了如何从中导出逆转置矩阵。

    还要注意,当比例统一时,您可以简单地将原始矩阵作为普通矩阵传递。想象一下同一个球体沿所有轴均匀缩放,表面不会拉伸或弯曲,法线也不会。

    【讨论】:

      【解决方案4】:

      如果模型矩阵由平移、旋转和缩放组成,则不需要进行逆转置来计算法线矩阵。只需将法线除以平方比例并乘以模型矩阵,我们就完成了。您可以将其扩展到具有垂直轴的任何矩阵,只需计算您正在使用的矩阵的每个轴的平方比例。

      详情我写在我的博客里:https://lxjk.github.io/2017/10/01/Stop-Using-Normal-Matrix.html

      【讨论】:

      • 对于任何正交变换 M(例如旋转),逆转置 M^(-T) = M。统一缩放只会改变法线的大小,而不是方向。翻译作为最后一个操作不会影响上部 3x3 部分,因此它没有实际意义。您应该使用反转置的所有其他内容 - 为什么不使用适用于 所有 情况的最通用解决方案?
      • 当然你不需要为统一比例做任何特别的事情。对于非均匀比例,此方法更快,然后将正常矩阵发送到着色器或在着色器中进行逆转置。对于模型矩阵,这是一种常见情况。如果模型矩阵不满足条件,那么不幸的是我们不得不回退到反转置。
      • 计算一个 3x3 逆转置来转换可能数千个顶点法线是无关紧要的。旋转后的非通知比例不会保留正常方向。
      • 这是 2 条指令 (mul, div) 与 8 条指令 (3 cross, 1 dot, 3 mul, 1 div),快了 4 倍以上。 (这两种方法最后都需要一个矩阵乘法,所以忽略那部分)这有多重要取决于您的应用程序。正确性在我的博文中得到了证明,有兴趣的可以看一下,和3x3反转置的结果一模一样。
      • 使用 3x3 矩阵转换法线是一次矩阵向量乘法(GLSL 中的 3 点积)——在 SIMD GPU 上最多需要 3 次乘法。大部分成本来自于在乘法之后对结果向量进行重新归一化 - GPU 在这方面特别快(点、rsqrt、点) - rsqrt 可能是最昂贵的,但有一些技巧可以在硅中完成( h14s.p5r.org/2012/09/0x5f3759df.html)
      【解决方案5】:

      不明白为什么在与模型矩阵相乘之前不将方向向量的第 4 个元素归零。不需要反转或转置。将方向向量视为两点之间的差。将这两个点与模型的其余部分一起移动 - 它们与模型的相对位置仍然相同。取两点之差得到新方向,第四个元素抵消为零。便宜很多。

      【讨论】:

      • Modelview 矩阵也可以缩放,包括在不同方向上的不同数量,不一定与轴定向。即左上角的 3x3 不一定是正交的。如果您将自己限制为特殊的正交矩阵,那么您的解决方案就足够了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-08
      • 2014-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多