转:对于法线贴图的深入研究
转:对于法线贴图的深入研究
原文地址: http://blog.csdn.net/debugconsole/article/details/18990153
正文
前几篇文章写过有关法线贴图
的内容,这次文章将讨论其原理及相关优化。回过头来看一下原来的文章真有种想删掉的感觉。。。
为什么叫法线贴图
,我们知道法线
(Normal
)是垂直于一个面的直线,通过计算光线与这条法线的角度就可以知道与面的角度,进而可以计算出面应得到的颜色值。如果我们知道物体每个面的法线就能实现对这个物体进行光照渲染。但是一堵墙也许只有四个顶点,也就是只有一个面,它最终的渲染效果将会非常单一,假设这堵墙上有更多的砖的凹凸痕迹,我们增样实现仅用四个顶点渲染出立体感很强,细节层次感很强的这堵墙呢,答案就是法线贴图
(Normal Map
)。
在法线贴图
技术中,我们就是通过把墙面的每个像素的法线存储在一张纹理中,渲染的时候根据每个像素的法线确定他们的阴暗程度,而这张法线贴图
是可以用photoshop
软件从一张墙的纹理生成对应的法线贴图的
。到此,熟悉法线贴图
的朋友会对以上内容很熟悉的。
试想在渲染过程中,如果把每个法向量都转换到世界空间
中跟光向量计算角度的话,那么这么多的像素法向量肯定影响性能,但是如果把光向量转换到法向量所在的空间中,岂不快哉?因此我们这里提到一个正切空间
(Tangent Space
)。正切空间
就是法向量所在的空间,在这个坐标系中,法向量作为高度轴,类似Z
轴。再找到其他的两个轴问题就会变得简单,但恰好这里是比较麻烦的。
其实我们已经知道高度轴
了,再找到一个轴,第三个轴就可以通过已知的两个轴做叉乘得到。我们要找的那个轴就叫做 正切向量
(Tangent Vector
)。正切向量是需要平行于法向量的平面的。
明白了这些问题就来实际的操作:
在正切空间
中,三个基向量分别叫做T
、B
、N
,T
代表Tangent
,B
代表Binormal
,N
代表Normal
。在Texture Space
(纹理空间
中), 两个二维基向量分别叫做U
、V
,T
就可以映射到纹理空间中的U
,B
就可以映射到纹理空间
中的V
。下面我们来推导一下三个向量的计算公式:
假设一个世界空间
中一点pi
,其纹理坐标为ui
,vi
,则:
pi = ui.T + vi.B
一个面有三个点p1
,p2
,p3
,则:
p1 = u1.T + v1.B
p2 = u2.T + v2.B
p3 = u3.T + v3.B
等价形式:
p2 - p1 = (u2 - u1).T + (v2 - v1).B
p3 - p1 = (u3 - u1).T + (v3 - v1).B
最终的变换形式:
(v3 - v1).(p2 - p1) - (v2 - v1).(p3 - p1)
T = -----------------------------------------
(u2 - u1).(v3 - v1) - (v2 - v1).(u3 - u1)
(u3 - u1).(p2 - p1) - (u2 - u1).(p3 - p1)
B = -----------------------------------------
(v2 - v1).(u3 - u1) - (u2 - u1).(v3 - v1)
而N
轴可以由两轴叉乘得到:
N = cross(T, B)
写成TBN矩阵的形式:
|Tx Bx Nx|
|Ty By Ny|
|Tz Bz Nz|
正切空间
到世界空间
转换:
VWorld = TBN·VTangent = VTangentT·TBNT
世界空间
到正切空间
转换:
VTangent = TBN-1·VWorld = VWorldT·TBN-T
而刚才说过 N
我们是可以存储到纹理中,是已知的。所以只需要找到每个面的三个点来计算T
,要知道这种计算式相当耗时间的,所以对于T
或者B
的计算,我们依然可以选择预先处理的办法,即存储在模型中,而大部分建模软件中的导出插件都具备这样的功能,即导出信息中包含Tangent
分量。如果非要自己计算也是可以的,但是我们会遇到一个问题:
每个面即每个三角形有三个点,通过这三个点计算每个点的T
向量,但是如果一个点被两个以上的面所共用,我们应该选择哪个面作为计算的依据?答案是,先分别计算每个面中该点的T
向量,然后得出平均值,更严格的来讲应该加权求均值,这里的权应该怎样计算?获许是根据面的角度吧,我没有具体实现过,所以不确定。
有人说,对于xyz
严格对齐的模型,时刻以人工指定其T
值的,但是这样也有漏洞,比如一面对其x
轴的墙朝北,显示正常,同时朝南的墙也就会不正常。
通过这个方法,我得到了一个启发,我们为什么要费这么大劲算TBN
空间?不就是为了把光线转换到TBN
空间中,然后和法向量做角度计算确定颜色值吗?我们完全可以直接在法向量所在的空间中定义光线的方向啊!
计算TBN
的部分shader
代码:
float3 normal = tex2D(NormapTex,inTxr) * 2 - 1;
float3 TBNLightPos = mul(lightPos,TBN_matrix);
float DiffuseAttn = clamp(0, 1,dot(normal, TBNLightPos ));
不计算TBN
直接指定光向量部分shader
代码:
float3 normal = tex2D(NormapTex,inTxr) * 2 - 1;
float DiffuseAttn = clamp(0, 1,dot(normal, float3(1,-1,1)));
省去了TBN的计算,性能下降几乎为0。
但是这样做有缺点,就是不能实时更新光源位置。这里我指定的float3(1,-1,1)
,是比较普遍适用的,它表示光源从斜45
度方向向下照射。效果还是可以接受的。
以下是未使用法线贴图效果:
以下是使用不计算TBN空间的法线贴图效果: