在计算光照时,大部分情况下我们在世界坐标下使用光照方向和光线方向:
// 光线方向
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
// 视线方向
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
在使用法线贴图(法线纹理)时,通常需要将光线方向和视线方向变换到顶点切线空间下进行光照计算。那么,如何将光线方向和视线方向变换到顶点切线空间呢?
- 首先,我们将光线和视线方向变换到顶点模型空间,可以使用 Unity 内置方法
ObjSpaceLightDir
和ObjSpaceViewDir
:
// 模型空间的光线方向
float3 lightDir = ObjSpaceLightDir(v.vertex);
// 模型空间的视线方向
float3 viewDir = ObjSpaceViewDir(v.vertex);
这里的 v.vertex
由顶点的 POSITION 语义指定,是顶点在模型空间下的坐标。
- 将上面计算出的
lightDir
和viewDir
从 模型空间 变换到顶点切线空间,我们知道变换无非就是右乘一个矩阵。那么如何确定这个变换矩阵呢?
顶点切线空间 的定义是:以顶点切线方向为 x 轴,顶点法线方向为 z 轴,顶点副法线方向为 y 轴得到的坐标空间,也就是说顶点切线空间下的顶点切线为 (1,0,0), 副法线为 (0,1,0),法线为(0,0,1);
假定在模型空间下,顶点切线、副法线和法线的坐标分别为 =(,,), =(,,), =(,,),变换矩阵 M 应该满足 = (1,0,0), = (0,1,0), = (0,0,1);
看起来不是很好计算 的各行各列的值。考虑另外一个变换矩阵 为从顶点切线空间到模型空间的变换矩阵,那么很明显 是 的逆矩阵,我们可以先计算 ,再对其求逆就可以得到,设
顶点切线空间的切线、副法线、法线变换到模型空间(右乘)后的值分别为=(,,), =(,,), =(,,),也就是有
展开计算,有,,
这样,就得到了模型空间到顶点切线空间的变换矩阵为
那么如何求 的逆矩阵呢?考虑到 =(,,), =(,,), =(,,)都是单位向量,以为例,有
且 因 与,互相垂直,也就是任意两者的 点积 为0,也就是如下的3个结果成立:
很容易可以推导出
可知 是正交矩阵,则它的逆也是正交矩阵,所以,很容易得到
也就是将模型坐标下的切线、副法线、法线按照行排列得到。