1.UnityObjectToWorldNormal
用于将object space下的normal向量转换到world space下。
// 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 }
其中,UNITY_ASSUME_UNIFORM_SCALING宏显然是优化用的,即如果是统一scale变换的情况下,直接用object to world space的矩阵就可以了,不用再去特意计算normal变换的矩阵;否则,根据Unity提供的注释,计算出变换后的normal:
\[(n')^T = (M^{-1})^T \cdot n^T = (n \cdot M^{-1})^T \]
2.DotClamped
用于将两个向量的点积进行clamp,使其值位于0~1之间。
inline half DotClamped (half3 a, half3 b) { #if (SHADER_TARGET < 30) return saturate(dot(a, b)); #else return max(0.0h, dot(a, b)); #endif }
3.EnergyConservationBetweenDiffuseAndSpecular
用来计算一个object的diffuse color的能量保存程度,保存程度越大,那么反射出来的diffuse color越弱,通常用来解决一个object的diffuse color + spec color过亮的问题(例如亮度超过入射光的强度)。
// Helper functions, maybe move into UnityCG.cginc half SpecularStrength(half3 specular) { #if (SHADER_TARGET < 30) // SM2.0: instruction count limitation // SM2.0: simplified SpecularStrength return specular.r; // Red channel - because most metals are either monocrhome or with redish/yellowish tint #else return max (max (specular.r, specular.g), specular.b); #endif } // Diffuse/Spec Energy conservation inline half3 EnergyConservationBetweenDiffuseAndSpecular (half3 albedo, half3 specColor, out half oneMinusReflectivity) { oneMinusReflectivity = 1 - SpecularStrength(specColor); #if !UNITY_CONSERVE_ENERGY return albedo; #elif UNITY_CONSERVE_ENERGY_MONOCHROME return albedo * oneMinusReflectivity; #else return albedo * (half3(1,1,1) - specColor); #endif }
我们可以这样使用:
float oneMinusReflectivity; albedo = EnergyConservationBetweenDiffuseAndSpecular(albedo, specColor, oneMinusReflectivity); float3 diffuse = albedo * lightColor * DotClamped(lightDir, i.normal); float3 specular = specColor * lightColor * pow(DotClamped(halfVector, i.normal), _Smoothness); return float4(diffuse + specular, 1);
4.DiffuseAndSpecularFromMetallic
用于根据一个object的金属化程度,计算出它的diffuse和spec分量。金属化程度越高,spec分量越大,diffuse分量越小。
inline half OneMinusReflectivityFromMetallic(half metallic) { // We'll need oneMinusReflectivity, so // 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic) // store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then // 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) = // = alpha - metallic * alpha half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a; return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec; } inline half3 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic); oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic); return albedo * oneMinusReflectivity; }
值得注意的是,这里的specColor是API返回的,而并非是我们事先设置的。我们可以这样使用:
float oneMinusReflectivity; albedo = DiffuseAndSpecularFromMetallic(albedo, metallic, specColor, oneMinusReflectivity); float3 diffuse = albedo * lightColor * DotClamped(lightDir, i.normal); float3 specular = specColor * lightColor * pow(DotClamped(halfVector, i.normal), _Smoothness); return float4(diffuse + specular, 1);