Unity 常用Light API 使用简介

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);

如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路-

posted @ 2021-03-02 00:15  异次元的归来  阅读(204)  评论(0编辑  收藏  举报