在光照运算中,我们需要将模型的顶点法线转换到世界空间坐标,通常Unity给我们提供了unity_ObjectToWorld矩阵方便我们进行转换,但是这种转换仅适用于等比缩放的模型,如果是非等比缩放的模型转换后顶点法线与切线不再垂直,原因如下:
上图中x轴缩短为原来的1/2,法线\(\vec{n}\)与切线\(\vec{t}\):(p3-p2),不再垂直
对于切线它始终由p3-p2来表示,所以p2怎么变,都不会影响到切线,这里为了简化推导我们设unity_ObjectToWorld为M,\(\vec{n}\)转世界空间矩阵为G,我们最终的目的是希望转换后的\(\vec{t^{'}}\)与\(\vec{n^{'}}\)依旧保持垂直关系,所以
\(M\vec{t}\cdot G\vec{n}=0\),
用矩阵的写法表示
\(\vec{t^{'}}\cdot \vec{n^{'}}=(MT)\cdot (GN)=0\) (向量转成列矩阵形式,用其大写字母表示)
因为\(\vec{t^{'}}\cdot \vec{n^{'}}\)用矩阵表示为\(T^{T}N\),所以
\(T^{T}N=(MT)\cdot (GN)=(MT)^{T}(GN)=T^{T}M^{T}GN\)
(注意运算符号从点乘变成了矩阵乘法,两个向量点乘相当于第一个矩阵转置乘以第二个矩阵)
所以 \(M^{T}G=I\)
所以\(G=(M^{T})^{-1}=(M^{-1})^{T}\) (满足矩阵转置运算与逆运算可以交换顺序的性质)
--------------------------------------------------------------------------------
-- 这里简单证明一下该性质
-- 因为 \((MM^{-1})^{T}=(M^{-1})^{T}M^{T}=I\)
-- 所以 \((M^{-1})^{T}=(M^{T})^{-1}\)
--------------------------------------------------------------------------------
因为M=unity_ObjectToWorld,所以M-1=unity_WorldToObject
所以G=unity_WorldToObject的转置,代码写作:transpose((float3x3)unity_WorldToObject)
所以当我们需要把非等比缩放的模型的法线转到世界空间时代码如下
i.normal = normalize(mul(transpose((float3x3)unity_WorldToObject),v.normal));
Unity为我们封装了一个统一的函数UnityObjectToWorldNormal(in float3 norm),方便我们把模型法线转到世界空间
i.normal = UnityObjectToWorldNormal(v.normal);
它定义在UnityCG.ginc中,原码如下:
// Transforms normal from object to world space inline float3 UnityObjectToWorldNormal( in float3 norm ) { #ifdef UNITY_ASSUME_UNIFORM_SCALING return UnityObjectToWorldDir(norm); #else // mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)} return normalize(mul(norm, (float3x3)unity_WorldToObject)); #endif } // Transforms direction from object to world space inline float3 UnityObjectToWorldDir( in float3 dir ) { return normalize(mul((float3x3)unity_ObjectToWorld, dir)); }
它的实现原理正如我们推导的结果一样,只是它更加巧妙的使用
normalize(mul(norm, (float3x3)unity_WorldToObject));
代替了
normalize(mul(transpose((float3x3)unity_WorldToObject),normal));
从而提升了运算效率(省掉了转置计算)
原理如下:
向量在左
\(\begin{bmatrix}
n_{x} &n_{y} &n_{z}
\end{bmatrix}\begin{bmatrix}
G_{11} &G_{12} &G_{13} \\
G_{21} &G_{22} &G_{23} \\
G_{31} &G_{32} &G_{33}
\end{bmatrix}\)=
\(\begin{bmatrix}
(n_{x},n_{y},n_{z})\cdot (G_{11},G_{21},G_{31}) & (n_{x},n_{y},n_{z})\cdot (G_{12},G_{22},G_{32}) &(n_{x},n_{y},n_{z})\cdot (G_{13},G_{23},G_{33})
\end{bmatrix}\)
向量在右
\(\begin{bmatrix}
G_{11} &G_{12} &G_{13} \\
G_{21} &G_{22} &G_{23} \\
G_{31} &G_{32} &G_{33}
\end{bmatrix}\begin{bmatrix}
n_{x}\\ n_{y} \\n_{z}
\end{bmatrix}=\begin{bmatrix}
(G_{11},G_{12},G_{13})\cdot (n_{x},n_{y},n_{z})\\
(G_{21},G_{22},G_{23})\cdot (n_{x},n_{y},n_{z})\\
(G_{31},G_{32},G_{33})\cdot (n_{x},n_{y},n_{z})
\end{bmatrix}\)
通过上面的观察可得:
\(\begin{bmatrix}
G_{11} &G_{12} &G_{13} \\
G_{21} &G_{22} &G_{23} \\
G_{31} &G_{32} &G_{33}
\end{bmatrix}^{T}\begin{bmatrix}
n_{x}\\ n_{y} \\n_{z}
\end{bmatrix}=\begin{bmatrix}
n_{x} &n_{y} &n_{z}
\end{bmatrix}\begin{bmatrix}
G_{11} &G_{12} &G_{13} \\
G_{21} &G_{22} &G_{23} \\
G_{31} &G_{32} &G_{33}
\end{bmatrix}\)
文章评论