法线贴图
法线贴图的主要原理是改变贴图的法线从而改变物体的颜色变化,产生一种凹凸感,我们需要弄明白如何将法线进行改变
重要概念
- 法线贴图:由于需要对物体的法线进行改变,我们需要对改变的法线进行存储,贴图用RGB存储颜色,法线贴图用RGB存储法线方向
- 切线空间:切线空间主要用于法线贴图的坐标系确立,由于物体在世界坐标系是不断变化的,我们没法使用世界坐标系作为法线贴图的坐标系,需要建立一个可以确认物体某个点的法线是固定不变的坐标系,这样我们才能对法线做出正确的变换。我们将物体的法线(N),顶点的切线(T)以及法线切线叉乘确认的副切线(B)作为形成的[T,B,N]矩阵作为切线空间的坐标系,而法线贴图的法线就是定义在这个坐标系下的
- 顶点切线、副切线的获取:三个顶点构成的平面和切线副切线的平面是共面的,如下图,而顶点坐标都是已知的,我们可以推倒出切线,副切线,Unity中会将切线信息存储在顶点中,副切线通过叉乘获得 具体推倒过程
- 正交基与坐标系转换: TBN矩阵的每个向量都需要进行归一化形成正交基,正交基的特点是每个基向量都是单位向量且相互垂直,这样的矩阵有个好处是逆矩阵和转置矩阵是一致的。向量计算是要在同一个坐标系下进行的,需要进行坐标系转换,TBN矩阵是切线空间的基向量在世界空间的表示,世界空间的向量左乘TBN可以实现向量从世界空间转换到切线空间,切线空间左乘TBN的逆矩阵可以实现向量从切线空间转换到世界空间。如切线空间中向量是(1,1,0)左乘(T,B,N)后 = T + B为世界坐标系下的两个基向量相加
注意点
- 法线贴图的法线是通过RGB存储的范围在(0,1)的区间,是(normal + 1 / 2)获得的,在实际运用过程中我们需要将采样值进行*2 -1恢复(-1,1)的法线方向
- 副切线B是通过叉乘获取到的,但是在左右手坐标系下叉乘遵循不同定则,在左手坐标系 B = cross(N,T),右手坐标系 T = cross(T,N)
- 貌似是为了代码互通,Tangent是以float4存储的,在得到副切线后需要乘上Tangent.w,获得最后的副切线方向,由于是左手坐标系Unity中,Tangent.w固定为-1
- Unity中 half3x3 TBN = half3x3(T, B, N)默认为行向量
- 由于存在一个顶点被多个平面所共享导致向量和切线不能相互垂直,导致TBN不是正交基,造成偏移,需要进行进行施密特正交化将切线和法向量变为相互垂直
实现思路与关键代码
有两种实现方式,一种是将向量计算转移到世界空间(或者模型空间),一种是都转移到切线空间,如果是第一种,我们将TBN传入片元着色器,对法线贴图的采样结果进行变换,如果是第二种,我们可以在顶点着色器就将光照进行变换传入片元着色器,这样就减少了很多矩阵运算。
以下为单平行光源仅计算漫反射情况的关键代码:
无法线贴图
1 | fixed4 frag(v2f i) : SV_Target{ |
第一种 转到世界空间
1 | fixed4 frag(v2f i) : SV_Target{ |
第二种 转到切线空间(推荐)
1 | v2f vert(a2v v) { |
效果
左边为实现效果,中间为Unity中的BumpMap效果,右边为不带法线贴图的效果
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.