Featured image of post Model矩阵对于法线的一些非预期变化

Model矩阵对于法线的一些非预期变化

object normal transform to world space by model matrix will case some mistakes

当进行model矩阵变换时,如果各个方向的分量变换(特指scale)不是统一的比例,会导致法线出现非预期的结果,如图。 将x变换为原来的 $1/2$ 时,法线会发生一次非预期的变化:法线的变换应该是x轴变化的倒数或者说逆。

$$ M = T_1T_2T_3 = S_1R_1P_1...$$$$M = S_1R_1S_2R_2 ...$$$$O = S^{-1}_1R_1S^{-1}_2R_2$$$$M^{-1} = R^{-1}_2 S^{-1}_2 R^{-1}_1 S^{-1}_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  
}

不难发现转换了乘法的顺序减少了一次转置操作,有利于编译器更好的实现代码。

Licensed under CC BY-NC-SA 4.0