object normal transform to world space by model matrix will case some mistakes
当进行model矩阵变换时,如果各个方向的分量变换(特指scale)不是统一的比例,会导致法线出现非预期的结果,如图。
将x变换为原来的 1/2 时,法线会发生一次非预期的变化:法线的变换应该是x轴变化的倒数或者说逆。
M=T1T2T3=S1R1P1...M=S1R1S2R2...O=S1−1R1S2−1R2M−1=R2−1S2−1R1−1S1−1(M−1)T=O所以将顶点法线转换到坐标空间的正确方法是
1
2
| i.normal = mul(transpose((float3x3)unity_WorldToObject), v.normal);
i.normal = normalize(i.normal);
|
当然,Unity官方提供了一个 UnityObjectToWorldNormal
函数进行这个操作,即有
1
| i.normal = UnityObjectToWorldNormal(v.normal);
|
查看定义
1
2
3
4
5
6
7
8
9
10
| // 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
}
|
不难发现转换了乘法的顺序减少了一次转置操作,有利于编译器更好的实现代码。