关于 BumpMap 和 NormalMap 原理细节中的 Tangent Space 和 TBN 矩阵

  这篇文章假使你已经对 BumpMap 和 NormalMap 的流程有基本的了解。

  这两天因为项目需要又复习了下 NormalMap 的原理细节,一年多以前细细看过一次,到现在忘了很多,可是当初又没有写笔记,所以在纠结细节的这两天因为数学差而特别痛苦。

  BumpMap 和 NormalMap 说起来大家都非常熟悉,但就是就我个人而言,初次分析背后的原理有很多容易混淆的地方,对于爱钻牛角尖的人很是折磨。

  BumpMap 出现早,主要使用灰度图(高度图),实时计算法线,过程就和生成法线图的过程一样,利用相邻像素点的高度差向量叉撑计算法线,这个过程比较简单没有异议,有了法线之后的计算和 NormalMap 一致,对于 BumpMap 的的理论,找到了一篇40多页的论文:A Practical and Robust Bump-mapping Technique for Today's GPU,我现在是实在没时间去琢磨看完它。

  对于 NormalMap 的使用,其中最重要的莫过于 Object Sapce (物体坐标系) 和 Tangent Space (切线坐标系,也称切线空间) 之间的转换过程,以及转换矩阵 TBN 矩阵。第一次接触是《Introduction to 3D Game Programming with DirectX 9.0c—A Shader Approach》这本书:假设 T, B, N 分别为相对于 Object Space 的沿着切线空间的 x, y, z 坐标轴的向量,那么从 Tangent Space 到 Object Space 的转换矩阵为:

                                  | Tx    Ty    Tz |

      MΔ = [T, B, N]T =   | Bx    By   Bz |

                                  | Nx    Ny   Nz |

假设 MΔ 为正交矩阵,那么从 Object Space 到 Tangent Space 的转换矩阵为:

                                                               | Tx    Bx   Nx |

     M = MΔ-1 = ([T, B, N]T)-1 = [T, B, N] =   | Ty    By   Ny |

                                                               | Tz    Bz   Nz |

  然后预生成每个顶点的 T, B, N。

  可能大家到这里就会觉得ok了,但是如果你看到了这篇文章《Derivation of the Tangent Space Matrix》,数学不好的人也许就会凌乱,因为在这里从 Tangent Spane 到 Object Space 的变换矩阵 M 与上面那本书里讲到的是相反的。即 MΔ = [T, B, N], M = MΔ-1 。(这篇文章讲解的 TBN 部分,与《Mathematics for 3D Game Programming and Computer Graphics》的第7.8节讲解的完全一致)。更有意思的是,我发现有人和我有同样的疑问,发在了 Ogre 论坛:http://www.ogre3d.org/forums/viewtopic.php?f=10&t=54827,比较早了2009年的,遗憾的是下面的回复都没有戳中他的点。

  那这两种说法到底谁正确呢?从 Tangent Spane 到 Object Space 的变换矩阵到底是 [T, B, N] 还是 [T, B, N]T 呢?答案是全都正确!

  因为看到这里线代学得不好的人很容易忽略一个条件就是:整个运算过程中使用的向量是行向量还是列向量行向量左乘矩阵,列向量右乘矩阵。假设 T, B, N 是相对于 Local Space 的 Tangent Space 自身的坐标轴, 那么相对 Tangent Space 自身三个坐标轴自然是 T,(1, 0, 0),B,(0, 1 , 0), N,(0, 0, 1) ,这时有以下公式

      对于使用行向量:[T,, B,, N,]T M = [T, B, N]T

                       =>   [(1, 0, 0),(0, 1 , 0), (0, 0, 1)] M = [T, B, N]T

                       =>   M = [T, B, N]T

      对于使用列向量:M [T,, B,, N,]= [T, B, N]

                       =>   M [(1, 0, 0),(0, 1 , 0), (0, 0, 1)][T, B, N]

                       =>   M = [T, B, N]

  很明显,《Introduction to 3D Game Programming with DirectX 9.0c—A Shader Approach》一书中使用的是行向量,从 Tangant Space 到 Object Space 的计算过程为:

      Vobject = Vtangent M = [Xtangent, Ytangent, Ztangent] [T, B, N]T;

      Vtangent = Vobject M-1 ,如果 M 正交,则有:

      Vtangent = Vobject M-1= Vobject MT

                  = [Xobject, Yobject, Zobject] [T, B, N]

                  = [Vobject dot T, Vobject dot B, Vobject dot N]

  而《Derivation of the Tangent Space Matrix》和 《Mathematics for 3D Game Programming and Computer Graphics》中使用的是列向量,尤其是前者的矩阵都是使用的左乘,那么计算过程为:

      VobjectT = M VtangentT = [T, B, N] [Xtangent, Ytangent, Ztangent]T;

      VtangentT = M-1 VobjectT ,如果 M 正交,则有:

      VtangentT = M-1 VobjectT = MT VobjectT

                   = [T, B, N]T [Xobject, Yobject, Zobject]T

                   = [VobjectT dot TT, VobjectT dot BT, VobjectT dot NT]

  通过以上对比,是不是一目了然,而且无论怎样转换,最终还有一个可以检验的就是,从 Object Space 到 Tangent Space 转换一个向量的最终结果一定是,该向量分别与 T, B, N 三个向量分别点乘,作为新的 Tangent Space 中向量的三个分量,从以上的Vtangent的最终结果就可以看到。

  额外补充,容易搞混的还有一点就是一旦假设 TBN 矩阵正交,那凌乱就来了,本来由于行向量和列向量就已经使 [T, B, N] 和 [T, B, N]T 的使用对于新手来说完全乱了阵脚,这个正交完全就是来捣乱的,所以建议新手完全忽略 TBN 矩阵的正交,记住 M = MΔ-1 就够了。Derivation of the Tangent Space Matrix》和 《Mathematics for 3D Game Programming and Computer Graphics》这两个都是到最后才说到正交化 TBN 概念,前面都是硬算 MΔ-1,所以它们介绍的理论更纯正,但细节也更复杂些。

  好了,以上就是这两天纠结理解的结果,下一篇我将再分享一下根据顶点位置和 uv 坐标计算具体的 TBN 矩阵时一些细节的理解。

 
posted @ 2013-06-04 00:52  yaukey  阅读(3404)  评论(0编辑  收藏  举报