unity urp 实现车漆效果

Posted 暮志未晚Webgl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity urp 实现车漆效果相关的知识,希望对你有一定的参考价值。

车漆的效果除了基础的pbr效果,上面还可以添加两层额外的效果。一层是清漆的效果,另一层就是薄膜干涉的效果。

这个就是薄膜干涉的效果,可以明显的看出来颜色被分散开来。


这里图片是取自https://zhuanlan.zhihu.com/p/67763458 大佬的知乎。

还有一层就是清漆的效果,也就是ClearCoat,也叫透明图层,这个原理就是为了模拟车上面喷的那层漆的效果。这个效果就实现起来比较简单,额外的增加了一层效果,

具体实现,涂层清漆的效果的之前在皮肤的那一章使用过,我这里不在介绍。
接下来,我们要使用unity内置的方式取实现镭射的效果。

// Evaluate the reflectance for a thin-film layer on top of a dielectric medum.
real3 EvalIridescence(real eta_1, real cosTheta1, real iridescenceThickness, real3 baseLayerFresnel0, real iorOverBaseLayer = 0.0)

    real3 I;

    // iridescenceThickness unit is micrometer for this equation here. Mean 0.5 is 500nm.
    real Dinc = 3.0 * iridescenceThickness;

    // Note: Unlike the code provide with the paper, here we use schlick approximation
    // Schlick is a very poor approximation when dealing with iridescence to the Fresnel
    // term and there is no "neutral" value in this unlike in the original paper.
    // We use Iridescence mask here to allow to have neutral value

    // Hack: In order to use only one parameter (DInc), we deduced the ior of iridescence from current Dinc iridescenceThickness
    // and we use mask instead to fade out the effect
    real eta_2 = lerp(2.0, 1.0, iridescenceThickness);
    // Following line from original code is not needed for us, it create a discontinuity
    // Force eta_2 -> eta_1 when Dinc -> 0.0
    // real eta_2 = lerp(eta_1, eta_2, smoothstep(0.0, 0.03, Dinc));
    // Evaluate the cosTheta on the base layer (Snell law)
    real sinTheta2Sq = Sq(eta_1 / eta_2) * (1.0 - Sq(cosTheta1));

    // Handle TIR:
    // (Also note that with just testing sinTheta2Sq > 1.0, (1.0 - sinTheta2Sq) can be negative, as emitted instructions
    // can eg be a mad giving a small negative for (1.0 - sinTheta2Sq), while sinTheta2Sq still testing equal to 1.0), so we actually
    // test the operand [cosTheta2Sq := (1.0 - sinTheta2Sq)] < 0 directly:)
    real cosTheta2Sq = (1.0 - sinTheta2Sq);
    // Or use this "artistic hack" to get more continuity even though wrong (no TIR, continue the effect by mirroring it):
    //   if( cosTheta2Sq < 0.0 ) =>  sinTheta2Sq = 2 - sinTheta2Sq; => so cosTheta2Sq = sinTheta2Sq - 1 
    // ie don't test and simply do
    //   real cosTheta2Sq = abs(1.0 - sinTheta2Sq);
    if (cosTheta2Sq < 0.0)
        I = real3(1.0, 1.0, 1.0);
    else
    

        real cosTheta2 = sqrt(cosTheta2Sq);

        // First interface
        real R0 = IorToFresnel0(eta_2, eta_1);
        real R12 = F_Schlick(R0, cosTheta1);
        real R21 = R12;
        real T121 = 1.0 - R12;
        real phi12 = 0.0;
        real phi21 = PI - phi12;

        // Second interface
        // The f0 or the base should account for the new computed eta_2 on top.
        // This is optionally done if we are given the needed current ior over the base layer that is accounted for
        // in the baseLayerFresnel0 parameter:
        if (iorOverBaseLayer > 0.0)
        
            // Fresnel0ToIor will give us a ratio of baseIor/topIor, hence we * iorOverBaseLayer to get the baseIor
            real3 baseIor = iorOverBaseLayer * Fresnel0ToIor(baseLayerFresnel0 + 0.0001); // guard against 1.0
            baseLayerFresnel0 = IorToFresnel0(baseIor, eta_2);
        

        real3 R23 = F_Schlick(baseLayerFresnel0, cosTheta2);
        real  phi23 = 0.0;

        // Phase shift
        real OPD = Dinc * cosTheta2;
        real phi = phi21 + phi23;

        // Compound terms
        real3 R123 = clamp(R12 * R23, 1e-5, 0.9999);
        real3 r123 = sqrt(R123);
        real3 Rs = Sq(T121) * R23 / (real3(1.0, 1.0, 1.0) - R123);

        // Reflectance term for m = 0 (DC term amplitude)
        real3 C0 = R12 + Rs;
        I = C0;

        // Reflectance term for m > 0 (pairs of diracs)
        real3 Cm = Rs - T121;
        for (int m = 1; m <= 2; ++m)
        
            Cm *= r123;
            real3 Sm = 2.0 * EvalSensitivity(m * OPD, m * phi);
            //vec3 SmP = 2.0 * evalSensitivity(m*OPD, m*phi2.y);
            I += Cm * Sm;
        

        // Since out of gamut colors might be produced, negative color values are clamped to 0.
        I = max(I, float3(0.0, 0.0, 0.0));
    

    return I;

上面就是内置的实现镭射的函数,大家可以在unity的内置文件BSDF中找到,我们使用这个求出镭射效果的F项,将默认的F进行一个插值即可。

//unity内置的镭射效果的实现
float topIor = lerp(1.0, 1.5, ClearCoat); //镭射反射率,有涂层的话就提高反射率
float viewAngle = lerp(NoV,sqrt(1.0 + Sq(1.0/topIor) * (Sq(dot(N, V)) - 1.0)), ClearCoat); //视角方向
float3 F_Iridescence = EvalIridescence(topIor, viewAngle, IridescenceThickness, SpecularColor); //求出镭射的F
F = lerp(F, F_Iridescence, Iridescence); //和原来默认的进行插值

上面是在直接光中使用。我们需要增加两个配置项,Iridescence是控制镭射效果强度,IridescenceThickness是控制镭射的厚度

//unity内置的镭射效果的实现 在环境光漫反射里面,直接去修改SpecularColor的值
float topIor = lerp(1.0, 1.5, ClearCoat); //镭射反射率,有涂层的话就提高反射率
float viewAngle = lerp(NoV,sqrt(1.0 + Sq(1.0/topIor) * (Sq(dot(N, V)) - 1.0)), ClearCoat); //视角方向
float3 F_Iridescence = EvalIridescence(topIor, viewAngle, IridescenceThickness, SpecularColor); //求出镭射的F
SpecularColor = lerp(SpecularColor, F_Iridescence, Iridescence); //和原来默认的进行插值

而在间接光里面,选择的是直接将SpecularColor 修改掉的方式实现。

以上是关于unity urp 实现车漆效果的主要内容,如果未能解决你的问题,请参考以下文章

unity urp 实现衣服绒毛效果

unity urp 实现衣服绒毛效果

unity urp 实现衣服上面片的效果

unity urp 实现衣服上面片的效果

unity urp 实现雨水在物体上流动的效果

unity urp 实现雨水在物体上流动的效果