UnityShader32:PBR(一)

 

一、整装待发

1.1:万事俱备

UnityShader相关:

PBR相关:

1.2:只欠东风

准备好你的参考资料,以及……新建一个 Shader,就可以开始愉快的造轮子了:

Shader "Jaihk662/PBR"
{
    Properties
    {
        _Color("MainColor", Color) = (1, 1, 1, 1)
        _SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
        _Glossiness("Smoothness", Range(0, 1)) = 1                  //光滑度
        _Metallic("Metalness", Range(0, 1)) = 0                     //金属度
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #include "Lighting.cginc"

            float4 _Color;
            float4 _SpecularColor;
            float _Glossiness;
            float _Metallic;
            //……
            ENDCG
        }
    }
    FallBack "Specular"
}

金属度(Metalness)和光滑度(Smoothness)这两个万分重要的属性当然是必不可少的

 

 

二、重要属性与方法

对于基础的数据结构和方法,可以提前准备好,包括但不限于:

  • 光照方向 I:lightDir
  • 法线方向 n:normal
  • 视角方向 v:viewDir
  • 半角方向  h=\frac{v+I}{|v+I|}:halfDir
  • 切线与副切线:T\&B:tangentDir & bitangentDir
  • 粗糙度 roughness
  • 光照衰减 atten
struct appdata
{
    float4 vertex: POSITION;
    float3 normal: NORMAL;                  //法线
    float4 tangent: TANGENT;                //切线
    float2 uv: TEXCOORD0;                   //uv坐标
    float2 lightMapUV: TEXCOORD1;           //光照贴图uv坐标
    float2 dynLightMapUV: TEXCOORD2;        //动态光照贴图uv坐标
};

struct v2f
{
    float4 pos: SV_POSITION; 
    float2 uv: TEXCOORD0;                   //uv坐标
    float2 lightMapUV: TEXCOORD1;           //光照贴图uv坐标
    float2 dynLightMapUV: TEXCOORD2;        //动态光照贴图uv坐标

    float3 normalDir: TEXCOORD3;            //法线
    float3 posWorld: TEXCOORD4;             //当前像素所在世界空间坐标
    float3 tangentDir: TEXCOORD5;           //主切线
    float3 bitangentDir: TEXCOORD6;         //副切线
    LIGHTING_COORDS(7, 8)                   //Unity自带阴影
    UNITY_FOG_COORDS(9)                     //Unity内置雾效
};

v2f vert(appdata v)
{
    v2f o;           
    o.uv = v.uv;
    o.lightMapUV = v.lightMapUV;
    o.dynLightMapUV = v.dynLightMapUV;
    o.normalDir = UnityObjectToWorldNormal(v.normal);
    o.tangentDir = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz);
    o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w);

    o.pos = UnityObjectToClipPos(v.vertex);
    o.posWorld = mul(unity_ObjectToWorld, v.vertex);
    UNITY_TRANSFER_FOG(o, o.pos);
    TRANSFER_VERTEX_TO_FRAGMENT(o)
    return o;
}

fixed4 frag(v2f i): SV_Target
{
    //法线方向
    float3 normal = normalize(i.normalDir);
    //光源方向,_WorldSpaceLightPos0.xyz为当前Pass中主光源方向,_WorldSpaceLightPos0.w为1表示当前光源为点光源或聚光灯,为0表示当前光源为平行光
    float3 lightDir = normalize(lerp(_WorldSpaceLightPos0.xyz, _WorldSpaceLightPos0.xyz - i.posWorld.xyz, _WorldSpaceLightPos0.w));
    //光源反射向量
    float3 lightReflectDir = reflect(-lightDir, normal);
    //视角方向
    float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
    //视角反射方向
    float3 viewReflectDir = normalize(reflect(-viewDir, normal));
    //视线与光照半角方向
    float3 halfDir = normalize(viewDir + lightDir); 

    float NdotL = max(0.0, dot(normal, lightDir));
    float NdotH = max(0.0, dot(normal, halfDir));
    float NdotV = max(0.0, dot(normal, viewDir));
    float VdotH = max(0.0, dot(viewDir, halfDir));
    float LdotH = max(0.0, dot(lightDir, halfDir));
    float LdotV = max(0.0, dot(lightDir, viewDir)); 
    float RdotV = max(0.0, dot(lightReflectDir, viewDir));

    //得到光照衰减
    float attenuation = LIGHT_ATTENUATION(i);
    //光照衰减颜色
    float3 attenColor = attenuation * _LightColor0.rgb;
    //粗糙度
    float roughness = 1 - (_Glossiness * _Glossiness);
    roughness = roughness * roughness;

    float3 diffColor = _Color.rgb * (1 - _Metallic) ;
    float3 specColor = lerp(_SpecularColor.rgb, _Color.rgb, _Metallic * 0.5);

    fixed4 col = fixed4(diffColor + specColor, 1.0);
    return col;
}

使用了输入光滑度(Smoothness)的平方来重新计算粗糙度(roughness)可以得到一个不错的效果,而对于金属度(Metalness),它会同时影响漫反射和镜面反射的颜色

  • 对于非金属,它拥有绝对的漫反射以及镜面反射,它们全部来源于纹理输入
  • 对于金属,它几乎没有漫反射,因此漫反射项颜色往往用于驱动镜面反射

 

 

三、完整的反射方程

完整的 Cook-Torrance BRDF 模型的 PBR 方程如下:

L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i

在计算菲涅尔系数时,通常要考虑金属和非金属(电介质),对于金属或导体(Conductor)表面,基础反射率 F_0 的值需要进行混合,对应的伪代码就是:

vec3 F0 = vec3(0.04);
F0 = mix(F0, surfaceColor.rgb, metalness);

代入 F_0 求出对应的菲涅尔系数理应就是 k_{d} 项,但由于金属几乎是不产生漫反射的,所以在计算漫反射项时减去镜面反射的比例后,还要再乘以电介质的比例,这也可以算到 k_{d} 项中,得到 k_{d}=(1-F)(1-\text {metallic})

对于镜面反射项系数 k_s,由于 DFG 中菲涅尔项 F 本身就已经体现了镜面反射的比例,所以这里可以直接去掉,好了,这样去掉 k_s 和 k_{d} 项的 PBR 反射方程就是:

L_o(p,\omega_o) = \int\limits_{\Omega} ((1-F)(1-\text {metallic})\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i

2018 UnityStandard 在计算 BRDF 时将整体乘上了一个 \pi,这也是为什么很多时候自己实现的 BRDF 会显得过暗的原因

本章完整代码如下:

Shader "Jaihk662/PBR"
{
    Properties
    {
        _Color("MainColor", Color) = (1, 1, 1, 1)
        _SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
        _Glossiness("Smoothness", Range(0, 1)) = 1                  //光滑度
        _Metallic("Metalness", Range(0, 1)) = 0                     //金属度
        _Ior("Ior", Range(1, 4)) = 1.5                              //折射指数,用于计算基础反射率
        _LUT("LUT", 2D) = "white" {}
    }

    CGINCLUDE
    #pragma vertex vert
    #pragma fragment frag
    #include "UnityCG.cginc"
    #include "AutoLight.cginc"
    #include "Lighting.cginc"
    #include "UnityStandardBRDF.cginc" 

    float4 _Color;
    float4 _SpecularColor;
    float _Glossiness;
    float _Metallic;
    float _Ior;
    sampler2D _LUT;

    #define PI 3.1415926535
    #define CalculationKAverage(p) pow(p, 2) * 0.797884560802865
    #define CalculationKDirect(p) pow(p + 1, 2) / 8
    #define CalculationKIBL(p) pow(p, 2) / 8
    #define MixFunction(i, j, x)  j * x + i * (1.0 - x)
//--------------------------------------------------Distribution Begin--------------------------------------------------
    //Beckman NDF(D项)
    float BeckmannNormalDistribution(float roughness, float NdotH)
    {
        float roughnessSqr = roughness * roughness;
        float NdotHSqr = NdotH * NdotH;
        return max(0.000001, (1.0 / (PI * roughnessSqr * NdotHSqr * NdotHSqr)) * exp((NdotHSqr - 1) / (roughnessSqr * NdotHSqr)));
    }

    //GGX NDF(D项)
    float GGXNormalDistribution(float roughness, float NdotH)
    {
        float roughnessSqr = roughness * roughness;
        float NdotHSqr = NdotH * NdotH;
        float TanNdotHSqr = (1 - NdotHSqr) / NdotHSqr;
        return (1.0 / PI) * sqrt(roughness / (NdotHSqr * (roughnessSqr + TanNdotHSqr)));
    }
//--------------------------------------------------Distribution End--------------------------------------------------

//--------------------------------------------------Geometric Begin--------------------------------------------------
    //Schlick-Beckman Geometric(G项)
    float SchlickBeckmanGeometricShadowingFunction(float NdotL, float NdotV, float roughness)
    {
        float k = CalculationKDirect(roughness);
        float SmithL = NdotL / (NdotL * (1 - k) + k);
        float SmithV = NdotV / (NdotV * (1 - k) + k);
        float Gs = SmithL * SmithV;
        return Gs;
    }
//--------------------------------------------------Geometric End--------------------------------------------------

//--------------------------------------------------Fresnel Begin--------------------------------------------------
    float SchlickFresnel(float i)
    {
        float x = clamp(1.0 - i, 0.0, 1.0);
        float x2 = x * x;
        return x2 * x2 * x;
    }
    //Schlick-Fresnel Fresnel(F项)
    float3 SchlickFresnelFunction(float3 F0, float LdotH)
    {
        return F0 + (1 - F0) * SchlickFresnel(LdotH);
    }
    float SchlickFresnelFunctionSingle(float F0, float LdotH)
    {
        return F0 + (1 - F0) * SchlickFresnel(LdotH);
    }

    //Spherical-Gaussian-Fresnel Fresnel拟合(F项)
    float SphericalGaussianFresnelFunction(float3 F0, float LdotH)
    {	
        float power = ((-5.55473 * LdotH) - 6.98316) * LdotH;
        return F0 + (1 - F0) * pow(2, power);
    }
    //利用折射指数(Indices of Refraction, IOR)直接计算金属材质的F0
    float SchlickIORFresnelFunction(float ior, float LdotH)
    {
        float F0 = pow(ior - 1, 2)/pow(ior + 1, 2);
        return SchlickFresnelFunctionSingle(F0, LdotH);
    }
    //利用插值计算金属材质的F0,其中unity_ColorSpaceDielectricSpec.rgb为所有非金属基础反射率的平均值,约为0.04
    float SchlickLerpFresnelFunction(float LdotH)
    {
        float F0 = lerp(unity_ColorSpaceDielectricSpec.rgb, _Color, _Metallic);
        return SchlickFresnelFunctionSingle(F0, LdotH);
    }
    //DisneyDiffuseFresnel 漫反射F项
    float DisneyDiffuseFresnel(float NdotL, float NdotV, float LdotH, float roughness)
    {
        float FresnelLight = SchlickFresnel(NdotL); 
        float FresnelView = SchlickFresnel(NdotV);
        float FresnelDiffuse90 = 0.5 + 2.0 * LdotH * LdotH * roughness;
        return MixFunction(1, FresnelDiffuse90, FresnelLight) * MixFunction(1, FresnelDiffuse90, FresnelView);
    }
//--------------------------------------------------Fresnel End--------------------------------------------------
    ENDCG

    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        Pass
        {
            CGPROGRAM
            struct appdata
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;                  //法线
                float4 tangent: TANGENT;                //切线
                float2 uv: TEXCOORD0;                   //uv坐标
                float2 lightMapUV: TEXCOORD1;           //光照贴图uv坐标
                float2 dynLightMapUV: TEXCOORD2;        //动态光照贴图uv坐标
            };

            struct v2f
            {
                float4 pos: SV_POSITION; 
                float2 uv: TEXCOORD0;                   //uv坐标
                float2 lightMapUV: TEXCOORD1;           //光照贴图uv坐标
                float2 dynLightMapUV: TEXCOORD2;        //动态光照贴图uv坐标

                float3 normalDir: TEXCOORD3;            //法线
                float3 posWorld: TEXCOORD4;             //当前像素所在世界空间坐标
                float3 tangentDir: TEXCOORD5;           //主切线
                float3 bitangentDir: TEXCOORD6;         //副切线
                LIGHTING_COORDS(7, 8)                   //Unity自带阴影
                UNITY_FOG_COORDS(9)                     //Unity内置雾效
            };

            v2f vert(appdata v)
            {
                v2f o;           
                o.uv = v.uv;
                o.lightMapUV = v.lightMapUV;
                o.dynLightMapUV = v.dynLightMapUV;
                o.normalDir = UnityObjectToWorldNormal(v.normal);
                o.tangentDir = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz);
                o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w);

                o.pos = UnityObjectToClipPos(v.vertex);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);
                UNITY_TRANSFER_FOG(o, o.pos);
                TRANSFER_VERTEX_TO_FRAGMENT(o)
                return o;
            }

            fixed4 frag(v2f i): SV_Target
            {
                //法线方向
                float3 normal = normalize(i.normalDir);
                //光源方向,_WorldSpaceLightPos0.xyz为当前Pass中主光源方向,_WorldSpaceLightPos0.w为1表示当前光源为点光源或聚光灯,为0表示当前光源为平行光
                float3 lightDir = normalize(lerp(_WorldSpaceLightPos0.xyz, _WorldSpaceLightPos0.xyz - i.posWorld.xyz, _WorldSpaceLightPos0.w));
                //光源反射向量
                float3 lightReflectDir = reflect(-lightDir, normal);
                //视角方向
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
                //视角反射方向
                float3 viewReflectDir = normalize(reflect(-viewDir, normal));
                //视线与光照半角方向
                float3 halfDir = normalize(viewDir + lightDir); 

                float NdotL = max(0.00001, dot(normal, lightDir));
                float NdotH = max(0.00001, dot(normal, halfDir));
                float NdotV = max(0.00001, dot(normal, viewDir));
                float VdotH = max(0.00001, dot(viewDir, halfDir));
                float LdotH = max(0.00001, dot(lightDir, halfDir));
                float LdotV = max(0.00001, dot(lightDir, viewDir)); 
                float RdotV = max(0.00001, dot(lightReflectDir, viewDir));

                //得到光照衰减
                float attenuation = LIGHT_ATTENUATION(i);
                //光照衰减颜色
                float3 attenColor = attenuation * _LightColor0.rgb;
                //粗糙度
                float roughness = 1 - (_Glossiness * _Glossiness);
                roughness = roughness * roughness;

                float Fd = DisneyDiffuseFresnel(NdotL, NdotV, LdotH, roughness);
                float3 diffColor = _Color.rgb * (1 - _Metallic) * Fd;
                float3 specColor = lerp(_SpecularColor.rgb, _Color.rgb, _Metallic * 0.5);

                float3 D = specColor;
                float G = 1;
                float3 F = specColor;
                D *= GGXNormalDistribution(roughness, NdotH);
                G *= SchlickBeckmanGeometricShadowingFunction(NdotL, NdotV, roughness);
                F *= SchlickFresnelFunction(specColor, LdotH);

                float3 specularity = (D * G * F) / (4 * (NdotL * NdotV)) * PI;
                float3 directLightCol = (diffColor + specularity) * NdotL * attenColor;
				float4 finalCol = float4(directLightCol, 1);

                UNITY_APPLY_FOG(i.fogCoord, finalCol);
                fixed4 col = finalCol;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Specular"
}

你会发现金属度高的时候材质显得过黑,如果是这样那就对了,因为目前并没有考虑间接光照

 

 

四、法线分布函数(Normal Distribution Function, NDF)

依赖完整的 Cook-Torrance BRDF 模型,反射方程  L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i  中的 D 项就是法线分布函数(Normal Distribution Function)

其中最经典的莫过于之前的 Blinn-Phong 经验模型:D_{\text {blinn}}(h)=(n \cdot h)^{\text {gloss }},这个已经实现过不少次了,所以这次考虑使用一些更高端的模型

4.1 Beckman NDF

定义 \alpha 为粗糙度 roughness,有  D_{\text {Beckman}}(\alpha, h)=\frac{e^{\frac{(n\cdot h)^2 - 1}{​{\alpha}^2(n\cdot h)^2}}}{\pi{\alpha}^2(n\cdot h)^4}

Beckman NDF 额外的考虑了粗糙度值

CGINCLUDE
#define PI 3.1415926535
//Beckman NDF(D项)
float BeckmannNormalDistribution(float roughness, float NdotH)
{
    float roughnessSqr = roughness * roughness;
    float NdotHSqr = NdotH * NdotH;
    return max(0.000001, (1.0 / (PI * roughnessSqr * NdotHSqr * NdotHSqr)) * exp((NdotHSqr - 1) / (roughnessSqr * NdotHSqr)));
}
ENDCG

4.2 GGX NDF

定义 \alpha 为粗糙度 roughness,cos\theta = n\cdot h,有 D=\frac{\alpha^{2}}{\pi \cos ^{4} \theta_{m}\left(\alpha^{2}+\tan ^{2} \theta_{m}\right)^{2}}

最常用的经验公式,这也是 2018 UnityStandard 中使用的标准各向同性分布函数

//GGX NDF(D项)
float GGXNormalDistribution(float roughness, float NdotH)
{
    float roughnessSqr = roughness * roughness;
    float NdotHSqr = NdotH * NdotH;
    float TanNdotHSqr = (1 - NdotHSqr) / NdotHSqr;
    return (1.0 / PI) * sqrt(roughness / (NdotHSqr * (roughnessSqr + TanNdotHSqr)));
}

4.3 未完待续……

更多的经验方程可参考:https://www.jordanstevenstechart.com/physically-based-rendering

 

 

五、阴影遮蔽函数(Geometric Shadowing Function, GSF)

依赖完整的 Cook-Torrance BRDF 模型,反射方程  L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i  中的 G 项就是阴影遮蔽函数(Geometric Shadowing Function)

如果不想要计算这一项,那么可以默认值为1,所有的阴影遮蔽函数值域都为 [0, 1]

5.1 Schlick-Beckman GSF

定义 \alpha 为粗糙度 roughness,k = \alpha^2\sqrt{\frac{2}{\pi}},有  G_{Schlick-Beckman}(I, v)=\frac{(n \cdot I)(n \cdot v)}{((n \cdot I)(1-k)+k)((n \cdot v)(1-k)+k)}

当然这里的  k 可以说是一个折中,对于直接光和间接光,还可以针对性的使用下面两个公式

  • 针对光照时:k_{direct} = \frac{(\alpha + 1)^2}{8}
  • 针对 IBL 的重映射(Remapping):k_{IBL} = \frac{\alpha^2}{2}

其实本质上,G 项为入射光遮蔽和出射光遮蔽两者的乘积,也就是 G_{Schlick-Beckman}(I, v)=\frac{(n \cdot I)}{lerp(n \cdot I, 1, k)}\cdot \frac{(n \cdot v)}{lerp(n \cdot v, 1, k)}

#define CalculationKAverage(p) pow(p, 2) * 0.797884560802865;
#define CalculationKDirect(p) pow(p + 1, 2) / 8;
#define CalculationKIBL(p) pow(p, 2) / 8;
//Schlick-Beckman Geometric(G项)
float SchlickBeckmanGeometricShadowingFunction(float NdotL, float NdotV, float roughness)
{
    float k = CalculationKDirect(roughness);
    float SmithL = (NdotL) / (NdotL * (1 - k) + k);
    float SmithV = (NdotV) / (NdotV * (1 - k) + k);
    float Gs = (SmithL * SmithV);
    return Gs;
}

5.2 未完待续……

更多的经验方程可参考:https://www.jordanstevenstechart.com/physically-based-rendering

 

 

六、菲涅尔项(Fresnel Function)

依赖完整的 Cook-Torrance BRDF 模型,反射方程  L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i  中的 F 项就是菲涅尔项(Fresnel Function)

不必多说,大名鼎鼎的 Fresnel-Schlick 近似,它几乎是最常用的经验方程:F_{Schlick}(h, v, F_0) = F_0 + (1 - F_0) ( 1 - (h \cdot v))^5

6.1 Fresnel-Schlick 近似

Fresnel-Schlick 还有另一种拟合版本 Spherical-Gaussian Fresnel:F(v, h)=F_{0}+\left(1-F_{0}\right) 2^{(-5.55473(v \cdot h)-6.98316)(v \cdot h)},它的效率要高一些,这在 UE4 中被使用

float SchlickFresnel(float i)
{
    float x = clamp(1.0 - i, 0.0, 1.0);
    float x2 = x * x;
    return x2 * x2 * x;
}
//Schlick-Fresnel Fresnel(F项)
float3 SchlickFresnelFunction(float3 F0, float LdotH)
{
    return F0 + (1 - F0) * SchlickFresnel(LdotH);
}
//Spherical-Gaussian-Fresnel Fresnel拟合(F项)
float SphericalGaussianFresnelFunction(float3 F0, float LdotH)
{	
    float power = ((-5.55473 * LdotH) - 6.98316) * LdotH;
    return F0 + (1 - F0) * pow(2, power);
}

对于金属材质的 Fresnel 计算,需要额外基础反射率,有三个方案:

  1. 通过表面颜色(一般是反照率 Albedo)和非金属基础反射率的平均值 0.04 插值得到
  2. 利用折射指数(Indices of Refraction, IOR)直接计算,其中 IOR 由输入指定
  3. 直接将计算得到的高光颜色(specColor)作为基础反射率

对于①②的代码如下:

//利用折射指数(Indices of Refraction, IOR)直接计算金属材质的F0
float SchlickIORFresnelFunction(float ior, float LdotH)
{
    float F0 = pow(ior - 1, 2)/pow(ior + 1, 2);
    return SchlickFresnelFunctionSingle(F0, LdotH);
}
//利用插值计算金属材质的F0,其中unity_ColorSpaceDielectricSpec.rgb为所有非金属基础反射率的平均值,约为0.04
float SchlickLerpFresnelFunction(float LdotH)
{
    float F0 = lerp(unity_ColorSpaceDielectricSpec.rgb, _Color, _Metallic);
    return SchlickFresnelFunctionSingle(F0, LdotH);
}
float SchlickFresnelFunctionSingle(float F0, float LdotH)
{
    return F0 + (1 - F0) * SchlickFresnel(LdotH);
}

6.2 Disney BRDF Fresnel

在计算漫反射项 k_d 时,F 项的计算也可参考 Disney BRDF:

  • F = \left(1+\left(F_{D 90}-1\right)\left(1-n\cdot l\right)^{5}\right)\left(1+\left(F_{D 90}-1\right)\left(1-n\cdot v\right)^{5}\right),其中  F_{D 90}=0.5+2\cdot \text {roughness} \cdot (l\cdot h)

此时 k_{d}=F(1-\text {metallic}),这也是 2018 UnityStandard 的写法

#define MixFunction(i, j, x)  j * x + i * (1.0 - x)
float DisneyDiffuseFresnel(float NdotL, float NdotV, float LdotH, float roughness)
{
    float FresnelLight = SchlickFresnel(NdotL); 
    float FresnelView = SchlickFresnel(NdotV);
    float FresnelDiffuse90 = 0.5 + 2.0 * LdotH * LdotH * roughness;
    return MixFunction(1, FresnelDiffuse90, FresnelLight) * MixFunction(1, FresnelDiffuse90, FresnelView);
}

 

 

参考文章:

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值