1:什么是Tangent space?
切线空间概念定义:
切线平面概念:组成一个模型的所有Mesh网格三角形或者四边形中的任意单个顶点为空间原点,顶点的法线为N轴,过顶点原点垂直于顶点法线N轴的平面为切线平面;
切线空间概念:切线平面上过顶点原点,在切线平面上取该顶点所在的UV纹理轴相同方向的两轴分别作为TB轴,由TBN向量轴所张成的局部空间; 就叫做顶点切线空间;
上面就是一个切线空间的例子,对于切线空间:
- N:该顶点本身的法线方向,z轴
- T:该顶点的一条切线,但由于切线数量有无数条,其一般由模型给定,对应着UV图中的U,也就是使用和纹理坐标方向相同的那条
- B:由前两者叉乘得到,对应着UV图中的V
UV图:用于告知计算机,如何用2维的贴图包住3维的物体,本质上UV图提供了一种模型表面与纹理图像之间的连接映射关系,
也就是确定纹理图像上的每一个像素应该放置在模型表面的哪一个顶点上,如果没有UV图,多边形网格将不能被渲染出纹理,
其中U和V分别指的是纹理空间的水平轴和垂直轴
总结:
切线空间就是纹理空间,
二维纹理平面,经过贴图贴到三维模型表面的贴图的过程,也就是切线空间中,二维UV平面进行二维仿射变换的过程,
也同样是三维切线空间中二维UV平面进行三维的线性变换的过程;
我们看到的贴图效果,都是纹理在贴图之后的处于三维模型空间的贴图效果,
是每个顶点都经过了每个点特有的TBN矩阵的变换,变换到各自对应的三维模型表面的世界空间了;
反过来,世界空间中三维模型表面的顶点的UV坐标,乘以TBN的逆矩阵,进行二维平面的仿射变换,就又变换到了切线空间;
球形图中的顶点切线与顶点法线是处于正交切线空间的,是模型表面的UV纹理坐标,进行TBN矩阵的逆变换之后所返回的顶点切线空间;
注意:TBN矩阵可以是[顶点切线空间]<==>[顶点世界空间] 也可以是 [顶点切线空间]<==>[顶点模型空间]
常用的都是[顶点切线空间Tangent space]<==>[顶点世界空间World Space],
高度贴图中使用的颜色数据,也是顶点法线数据,而且是切线空间中的顶点法线数据;
UV图进行贴图的过程,就是UV图上每个纹理像素乘以每一个其各自不同的TBN矩阵,从而对应到该像素对应的模型顶点上,是从UV空间转换到模型空间的过程;
TBN矩阵本质上是一个线性变换的旋转缩放矩阵,不涉及到模型本身的位移移动,
而模型本身,会在世界空间,观察空间,和投影空间进行位移,所以世界矩阵,观察矩阵和投影矩阵都是Mat4的矩阵;
而TBN矩阵只是Mat3矩阵;
空间内点或者向量本身的位移,就涉及到齐次坐标;
模型纹理贴图之后,
顶点的UV坐标,存在于切线空间/纹理空间,同样的顶点的三维模型坐标,存在于模型空间之中,
同样的顶点,由纹理空间坐标,到模型空间的坐标,就是进行了一次UV三维线性变换(UV二维仿射变换),
其变换矩阵就是类TBN的Modle下TBN,不属于通常下的TBN;需要再乘以模型的世界空间转换矩阵WordMatrix;
当顶点是由纹理空间坐标,到模型空间的坐标,则是TBN矩阵;
注意:
顶点法线N轴一定垂直于过该顶点的切线平面,但是过顶点的切线平面不一定与顶点所在Mesh网格平面平行或重合,所以顶点法线 N轴不一定垂直与该顶点所在的Mesh网格平面,; TB二轴在正交化前与UV方向分别相同,故TBN正交化后TB二轴必然与uv方向是有所偏差的;
注意:
顶点法线与面法线是不同的法线不同的概念; 这个属于常见带有的误解,在此特意申明,顶点法线可以爱怎么调整就怎么调整,与面法线并无关联。面法线只是垂直于面的一条向量,规定了面的正反,而顶点法线才是用于光照信息的处理。即便是建模软件中,顶点法线的最初是的默认情况也并非是面法线的平均,只有当在建模软件中对物体进行了平滑着色后,才会根据面法线平均得到顶点法线(如blender中为shader smooth命令)。这其中的逻辑不要弄混。顶点法线与顶点所在的Mesh面片(三角形或四边形)不一定垂直;而且对于有纹理坐标变换的情形(图形/模型拉伸),T和B两轴甚至不会相互垂直,但是N与T或B都垂直。
注意:
Tangent Space与World Space,View Space其实是同样的概念;
模型Mesh网格顶点的切线空间,本质上就是模型的局部空间,就像透视投影空间Projection,
摄像机观察空间View,模型变换到通用的世界空间World,而作为模型的局部空间的切线空间也需要变换到模型空间用TBN;
2,为什么需要tangent space?
一个立方体的六个面不能应用同一张高度贴图,因为六个面的切线空间不一样;那为什么同一个立方体不同面的切线空间不一样呢?
所谓法线贴图的技术;
本身所用的高度贴图就是利用高度贴图中的[颜色]数据来标记[顶点法线]坐标;
在同一个坐标系内,一个立方体的6个面的法线(向量)本就不同;一个是+z,一个是-z;一个是+x,一个是-x,一个是+y一个是-y;6个面各不相同;
而同一张高度贴图的颜色数据即顶点法线数据是写好的固定的,当然不可能适应不同的6个面的法线适配;
此时若想要用同一个高度图来适配顶点数相同的立方体6个面,就需要用到切线空间和切线空间转换矩阵TBN矩阵;
无论如何,由于T、B向量是定义在顶点上的,这样即使经过刚体变换甚至变形,处理方式都是一致的。
而如果不在tangent space定义normal map,那么模型既无法复用,也无法改变方向和变形(或者说用起来很麻烦)。就如同在local space中定义顶点坐标而不在世界坐标中定义一样。
另一种说法:
要理解为什么要有切线空间,可以从法线贴图入手。众所周知,绝大部分的法线贴图,颜色都是偏蓝色的,这是因为法线贴图中存储的法线向量大部分都是朝向或者接近 z 轴的,即 (0,0,1)(0,0,1),换算到 RGB 中,就是偏向蓝色,即 (0.5,0,5,1)(0.5,0,5,1) (后面的 Shader 中有算法),这种贴图就是切线空间 (Tangent Space)下的贴图。这显然存在一个问题,想象一个位于世界坐标原点且没有进行任何变换的立方体,表面法线方向就有 6 个,因为有 6 个不同朝向的面(确切的说,可能是 12 个面,因为一个矩形一般由两个三角形组成),而且每个面完全相同,所以这时候我应该只需要一个面的法线贴图就可以了。但其实这时再用这种偏蓝色的法线贴图就不行了,因为立方体的上表面在世界空间的法线方向为 (0,1,0)(0,1,0),而在法线贴图中采样出来的法线基本都是接近于 (0,0,1)(0,0,1) 的,使用错误的法线会得到错误的光照结果。所以这时候需要做一张包含立方体所有面的法线信息的法线贴图,也就是模型空间 (Object Space)下的法线贴图,而这种贴图看起来就不单单是偏蓝色了,而是包含了多种颜色。
这样看起来好像也没什么问题,但其实用切线空间下的法线贴图要比用模型空间下的法线贴图要有一些优势:
- 可以复用:比如上文提到的立方体,如果每个面都完全相同,则可以只制作一个面的法线贴图,然后就可以复用到所有面上,类似的复用需求还有很多,这可以减小内存占用和包体大小。
- 纹理可以压缩:因为切线空间下,一般来说法线方向不会是朝向表面内部,即法线贴图中的 z 值不会是负数,而我们使用的法线又是归一化的,所以完全可以根据 x 和 y 的值来推导出 z 的值,所以贴图中只需要存储 x 和 y 的值即可,可进行纹理压缩
- 自由度很高。Tangent-Space Normal Map记录的是相对法线信息,这意味着,即便把该纹理应用到一个完全不同的网格上,也可以得到一个合理的结果。
- 可进行UV动画。比如,我们可以移动一个纹理的UV坐标来实现一个凹凸移动的效果,这种UV动画在水或者火山熔岩这种类型的物体会会用到。
- 可以重用Normal Map。比如,一个砖块,我们可以仅使用一张Normal Map就可以用到所有的六个面上。
- 可压缩。由于Tangent-Space Normal Map中法线的Z方向总是正方向的,因此我们可以仅存储XY方向,而推导得到Z方向。
综上所述,一般的法线贴图都是使用切线空间的,而直接使用切线空间下的法线贴图又会出现之前提到的立方体的那个问题,所以我们在使用前需要先进行切线空间相关的变换,把所需要的数据变换到同一个坐标空间下再进行计算(可以全部变换到世界空间也可以全部变换到切线空间)。
注意:切线空间作为模型空间的部分局部空间,当模型的部分身体结构比如手臂等发生移动,这部分模型顶点的模型空间坐标也就会发生变化,但是这部分模型顶点的切线空间不会随着手臂的移动而改变,这也是切线空间的用处之一;
另一种说法:
在3D世界中定了如此多的坐标系,每个坐标系当然都有它的用途。比如局部空间,或者叫模型空间,它的目的就是方便我们对3D模型进行建模。在这个空间中,我们不需要考虑该模型在场景中可能出现的位置、朝向等众多细节,而专注于模型本身。在世界空间中,我们关心的问题是场景中各个物体的位置、朝向,即如何构建场景,而不必关注摄像机的观察位置及其朝向。可见,一个坐标系的根本用途,即让我们在处理不同的问题时,能够以合适的参照系,抛开不相关的因素,从而减小问题的复杂度。
直观地讲,模型顶点中的纹理坐标,就定义于切线空间。普通2维纹理坐标包含U、V两项,其中U坐标增长的方向, 即切线空间中的tangent轴,V坐标增加的方向,为切线空间中的bitangent轴。模型中不同的三角形,都有对应的切线空间,其tangent轴和bitangent轴分别位于三角形所在平面上,结合三角形面对应的法线,我们称tangant轴(T)、bitangent轴(B)及法线轴(N)所组成的坐标系,即切线空间(TBN)。
1、 什么是Tangent space?
Tangent space和world space,view space其实是同样的概念,均是代表三维坐标系。在这个坐标系中, X轴对应纹理坐标的U方向,沿着该轴纹理坐标U线性增大。Y轴对应纹理坐标的V方向,沿着该轴纹理坐标V线性增大。Z轴则是UXV,垂直于纹理平面。
2、 为什么需要tangent space?
在normal map等技术中,存储在texture中的值是基于tangent space的法线。因此,当我们sample这些texure中的法线进行光照计算时,必须要统一到同一坐标系下结果才正确,这时候就需要切线空间(就像是所有的local space都要统一到world space一个道理。
那么为什么normal map里面存的法线信息是基于tangent space而不是基于local sapce呢?基于local space理论上是可以的,但是这样的normal map只能用于这一个模型,不同把这个normal map用于其他模型。比如说建模了一个人,并且生成了该模型基于local space的normal map,如果我们建模同样一个人,但是放的位置和角度和之前的不一样,那么之前的normal map就不可用了,因为local space并不一样。但如果我们normal map里存的是tangent space的normal的话,就不存在这个问题,因为办要模型一样,模型上每个点的tangent space就是一样的,所谓是以不变应万变。
3、怎样计算tangent space?
假设三角形三个坐标点以及纹理坐标点为P1(U1, v1)、P2(U2, V2)、 P3(U3, V3), 假设切线空间的三个基为T、B、N(T为切线方向,也就是U方向,B为副法线方向,也就是V方向),T与B都在三角形所在的平面上。(T、B、N分别为Tangent、Binormal、Normal)
P1 * T = U1;P2 * T = U2; P3 * T = U3;
P1 * B = V1; P2 * T = V3; p3 * T = V3;
由此可以解出T和B向量,由于N = T X B,因此便得到了该三角形的切线空间。
向量,经过线性变换不改变空间的基,所以可以用三个即使经过拉伸旋转(对折)的UV值求出UV空间的U也就是切线空间的T;
矩阵经过初等变换之后仍与原来矩阵等价;不改变矩阵的秩;
算法:
Tangent space在Bump Map中有着重要作用,通常需要把灯光转换到tangent space进行计算。对由参数方程计算出的规则曲面(比如,球体,圆环)来说,很容易通过方程计算出tangent space,但对任意的三角形网格来说,则没有那么简单。
Tangent space是一个三维空间。对3D空间中的一个顶点来说,切空间的三条座标轴分别对应该点的法线N,切线T,和副法线(binormal)B,显然,对不同的顶点来说,切空间是不同的。那么在已知三角形三个顶点及其纹理坐标的时候,如何计算出N,T,B呢?
目前已知的数据每个Mesh的三角形网格有三角形的三个顶点在世界坐标中的位置: P0, P1,P2, 以及相应的纹理坐标在纹理空间中的位置C0 (U0,V0),C1,C2,则有:
P10 = P1 – P0,
P20 = P2 - P0 ,
C10 = C1 – C0 = (U1-U0, V1-V0) = ( U10 ,V10)
C20 = C2 – C0.= (U2-U0, V2-V0) = ( U20 ,V20)
注意,P10在世界坐标中的方向和C10在纹理空间中的方向是一致的(这一点确实比较抽象,偶画图研究了好久才弄明白-_-),同样,P20和C20也是如此,发现这一点很重要,可以说是整个计算的基石。进一步来说,T,B分别和纹理坐标轴U,V是平行的。因此我们有:
P10 = U10T + V10B
P20 = U20T + V20B
把矢量展开得到:
两边乘以[C10 C20]的逆矩阵,最后得到
法线N = T x B
这样我们就得到了坐标从切空间转变到世界坐标下的变换矩阵M = [ T B N ],当然,更加常用的是M的逆矩阵。注意,这里计算得出的只是面法线,如果需要计算每个顶点的法线,则应该对共享该顶点的多个面的法线取均值,求出结果。