URP学习之六--URP下的PBR

上节我们学习了LitShader大致是怎样的起作用的,留下了PBR相关的疑问,这节我们来解答一下URP下的PBR究竟做了什么优化。

要知道做了什么优化,就必须知道原来的PBR是什么样子。

Unity原来的Standard是这样写的:

half4 BRDF1_Unity_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
    float3 normal, float3 viewDir,
    UnityLight light, UnityIndirect gi)
{
    float perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
    float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);

#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0

#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
    // The amount we shift the normal toward the view vector is defined by the dot product.
    half shiftAmount = dot(normal, viewDir);
    normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
    // A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
    //normal = normalize(normal);

    half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
#else
    half nv = abs(dot(normal, viewDir));    // This abs allow to limit artifact
#endif

    half nl = saturate(dot(normal, light.dir));
    float nh = saturate(dot(normal, halfDir));

    half lv = saturate(dot(light.dir, viewDir));
    half lh = saturate(dot(light.dir, halfDir));

    // Diffuse term
    half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;

    // Specular term
    // HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm!
    // BUT 1) that will make shader look significantly darker than Legacy ones
    // and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH
    float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
#if UNITY_BRDF_GGX
    // GGX with roughtness to 0 would mean no specular at all, using max(roughness, 0.002) here to match HDrenderloop roughtness remapping.
    roughness = max(roughness, 0.002);
    half V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
    float D = GGXTerm (nh, roughness);
#else
    // Legacy
    half V = SmithBeckmannVisibilityTerm (nl, nv, roughness);
    half D = NDFBlinnPhongNormalizedTerm (nh, PerceptualRoughnessToSpecPower(perceptualRoughness));
#endif

    half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later

#   ifdef UNITY_COLORSPACE_GAMMA
        specularTerm = sqrt(max(1e-4h, specularTerm));
#   endif

    // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value
    specularTerm = max(0, specularTerm * nl);
#if defined(_SPECULARHIGHLIGHTS_OFF)
    specularTerm = 0.0;
#endif

    // surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1)
    half surfaceReduction;
#   ifdef UNITY_COLORSPACE_GAMMA
        surfaceReduction = 1.0-0.28*roughness*perceptualRoughness;      // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
#   else
        surfaceReduction = 1.0 / (roughness*roughness + 1.0);           // fade \in [0.5;1]
#   endif

    // To provide true Lambert lighting, we need to be able to kill specular completely.
    specularTerm *= any(specColor) ? 1.0 : 0.0;

    half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
    half3 color =   diffColor * (gi.diffuse + light.color * diffuseTerm)
                    + specularTerm * light.color * FresnelTerm (specColor, lh)
                    + surfaceReduction * gi.specular * FresnelLerp (specColor, grazingTerm, nv);

    return half4(color, 1);
}

我们直接看最后color的计算:

Color = 漫反射颜色*(GI漫反射+光照颜色*漫反射项)+高光反射项*光照颜色*菲涅耳项+表面反射缩减参数*GI高光反射*菲涅耳插值

其中,漫反射项是DisneyDiffuse,由D(NDF)V(Visiblity)组成的高光反射项和后面的菲涅尔项是BRDF的核心,环境高光由GI高光反射颜色、表面反射缩减参数和菲涅尔插值(将specularColor当作F0,grazingTerm当作F90)共同决定。

 笔者看完两段代码做了个对比,发现环境光的计算URP基本上没有上面变化,关键是BRDF做了较大变化。

首先漫反射原来管线用的是Disney,而URP会将DirectBRDF所得漫反射和高光反射全部乘上一个radiance,radiance代表单位片元辐射量,具体计算就是光照、光照衰减和ndotl(重点),NdotL乘到BRDF漫反射项就是一个兰伯特(当然,这样是为了方便理解,其实按照辐射量的理解更好)。

然后就是BRDF高光项的计算URP做出了优化。BRDF中最重要的三个因子D、V、F(V是unity将G项和分母项进行了结合)中,V和F在URP做出了优化,URP中把VF的乘积综合到一个简化的公式中达到了优化的效果,我们可以对比一下公式:

内置管线Visibility:

 URP的VF:

 可以看出URP对于V上做了非常大的优化,至于这种优化的依据是什么,Unity官方也给出了指向:See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course

应该是Siggraph 2015的文章,文章的文件有点大,链接放这里:http://s2015.siggraph.org/sites/default/files/firstpages.reduced.pdf

有兴趣的小伙伴自己可以去看看。本来感觉这篇应该会写很多内容,后来看完之后发现一直贴代码会影响观感,于是还是自己总结出来直接放在这里好了(虽然总结的也不多~),这篇文章纯属是讲述URP Litshader过程中临时扩充出来的,就当做个记录吧,下一节我们学习什么呢,想了良久后,发现应该紧扣主题,所以我们下一节来看看URP的RenderFeature以及如何扩展URP(至于LitShader中其他的Pass一方面决定没什么必要讲,一方面时间也不是很充裕,应该会在其他系列再一起来学习:))。

各位小伙伴们如果有意见或者建议,可以在文章下方留言哦~

猜你喜欢

转载自www.cnblogs.com/shenyibo/p/12550937.html