美文网首页
Unity 5.x Standard Shader实现分析

Unity 5.x Standard Shader实现分析

作者: Orini | 来源:发表于2017-08-12 00:04 被阅读0次

    这篇文章用来讨论Unity的标准着色器实现,根据Unity 5.4.2版本的代码进行分析。Unity 5.3之前的版本的据说没有增加GGX的实现,所以Unity 5.4之后的版本都可以。
    Unity的Standard Shader具体实现,在UnityStandardBRDF.cginc文件中。代码中有三个BRDF的实现函数,对应不同的BRDF模型实现。这里主要分析第一个BRDF函数,也就是BRDF1_Unity_PBS,使用的BRDF模型的公式可以参考:http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html

    首先,我们要清楚BRDF1_Unity_PBS函数的依据,即根据Torrance-Sparrow 微表面模型的公式:
    f(l,v)=D(h)F(v,h)G(l,v,h)/(4(n⋅l)(n⋅v))
    以及BRDF公式:
    BRDF = kD / pi + kS * (D * V * F) / 4
    然后拆分公式,一项项的实现。

    D——微表面分布项
    V——遮挡可见性项
    F——菲涅尔反射项
    kD——漫反射系数
    kS——镜面反射系数
    Note:V(Visibility)项即G(l,v,h)/(4(n⋅l)(n⋅v))的集合。

    最简单的是菲涅尔因子的计算,在代码中实现:

    //菲涅尔项的计算
    //菲涅尔的近似公式为F=F0+(1-F0)*(1-(H*V))^5
    //F0是光线垂直入射的反射率
    inline half3 FresnelTerm (half3 F0, half cosA)
    
    {
    
    half t = Pow5 (1 - cosA);  // ala Schlick interpoliation
    
    return F0 + (1-F0) * t;
    
    }
    

    V项和D项,在这里出现了分支:
    如果UNITY_BRDF_GGX为真,V项和D项使用GGX的公式来实现。
    否则,V项和D项使用Smith-Beckmann和Blinn-Phong公式实现。
    Note:公式在上方链接中可查。
    分支选择代码如下:

    #if UNITY_BRDF_GGX
        half V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
        half D = GGXTerm (nh, roughness);
    #else
        // Legacy
        half V = SmithBeckmannVisibilityTerm (nl, nv, roughness);
        half D = NDFBlinnPhongNormalizedTerm (nh, PerceptualRoughnessToSpecPower(perceptualRoughness));
    #endif
    

    然后,依次分析四个函数。首先是SmithJointGGXVisibilityTerm ,Unity使用了简化版的近似公式。
    如下面代码所示:#if 0表示原始公式实现的这部分代码永远不会执行。

    inline half SmithJointGGXVisibilityTerm (half NdotL, half NdotV, half roughness)
    {
    //#if 0分支不会被执行,执行下面的简化分支
    #if 0
        //原始公式
        // Original formulation:
        //  lambda_v    = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
        //  lambda_l    = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
        //  G           = 1 / (1 + lambda_v + lambda_l);
    
        // Reorder code to be more optimal
        half a          = roughness;
        half a2         = a * a;
    
        half lambdaV    = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
        half lambdaL    = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
        //简化的可见性项
        // Simplify visibility term: (2.0f * NdotL * NdotV) /  ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));
        return 0.5f / (lambdaV + lambdaL + 1e-5f);  // This function is not intended to be running on Mobile,
                                                    // therefore epsilon is smaller than can be represented by half
    #else
        //上述公式的近似,简化了sqrt,数学不正确但足够接近
        // Approximation of the above formulation (simplify the sqrt, not mathematically correct but close enough)
        half a = roughness;
        half lambdaV = NdotL * (NdotV * (1 - a) + a);
        half lambdaL = NdotV * (NdotL * (1 - a) + a);
    
        return 0.5f / (lambdaV + lambdaL + 1e-5f);
    #endif
    }
    

    相关文章

      网友评论

          本文标题:Unity 5.x Standard Shader实现分析

          本文链接:https://www.haomeiwen.com/subject/cvqkrxtx.html