【Unity Shader入门精要】基学习于物理的渲染(四)

基于物理的渲染四(PBS)

简略实现PBS

Shader "Unlit/SelfPBR"
{
    Properties
    {
        //漫反射材质控制颜色
        _Albedo("Color",Color)=(1,1,1,1)
        //漫反射纹理
        _AlbedoTex("Albedo",2D)="white"{}

        //平滑度
        _Glossiness("Smoothness",Range(0.0,1.0))=0.7

        //高光反射材质控制颜色
        _SpecularCol("Specular Color",Color)=(0.2, 0.2, 0.2)
        //高光反射纹理
        _SpecularTex("Specular Tex",2D)="white"{}

        //自发光控制颜色
        _EmissionColor("Emission Color",Color)=(0,0,0)
        //自发光纹理
        _EmissionMap("Emission Map",2D)="white"{}

       //法线控制项
        _BumpScale("Bump Scale",float)=1.0
        //法线纹理
        _BumpMap("Normal Map",2D)="bump"{}

    }

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

        CGINCLUDE
        #include "UnityCG.cginc"
        #include "HLSLSupport.cginc"
        #include "AutoLight.cginc"
        #include "Lighting.cginc"

        fixed4 _Albedo;
        sampler2D _AlbedoTex;
        float4 _AlbedoTex_ST;

        half _Glossiness;

        fixed4 _SpecularCol;
        sampler2D _SpecularTex;

        fixed4 _EmissionColor;
        sampler2D _EmissionMap;

        float _BumpScale;
        sampler2D _BumpMap;
        float4 _BumpMap_ST;

        inline half SaturateAndDot(half3 v1,half3 v2)
        {
            return saturate(dot(v1,v2));
        }

        //Disney BRDF
        inline half3 DisneyDiffuseBRDF(half3 baseColor, half NdotL,half NdotV,half LdotH,half roughness)
        {
            half fd90 = 0.5 + 2 * roughness * LdotH * LdotH;
            half lightScatter = 1 + (fd90 - 1) * pow(1-NdotL,5);
            half viewScatter = 1 + (fd90 - 1) * pow(1-NdotV,5);

            return baseColor * UNITY_INV_PI * lightScatter * viewScatter;
        }


        inline half3 FresnelSchlick(half3 c,half HdotL)
        {
            half t = pow(1 - HdotL, 5);
            return c + (1 - c) * t;
        }

        inline half3 CustomFresnelLerp(half3 c0, half3 c1, half cosA)
        {
            half3 light = pow(1-cosA,5);
            return lerp(c0,c1,light);
        }

        //法线分布函数
        inline half GGX(half NdotH, half roughness)
        {
            half a2 = roughness * roughness;
            half d = (NdotH * a2 - NdotH) * NdotH + 1.0f;
            return UNITY_INV_PI * a2 / (d * d + 1e-7f);
        }
        
        //阴影遮挡函数
        inline half SmithJointGGX(half NdotL, half NdotV, half roughness)
        {
            half a2 = roughness * roughness;
            half lambdaV = NdotL * (NdotV * (1 - a2) + a2);
            half lambdaL = NdotV * (NdotL * (1 - a2) + a2);
            return 0.5f / (lambdaV + lambdaL + 1e-5f);          
        }

       

        struct a2v
        {
            float4 vertex:POSITION;
            float3 normal:NORMAL;
            float4 tangent:TANGENT;
            float4 texcoord:TEXCOORD;
        };

        struct v2f
        {
            float4 pos:SV_POSITION;
            float2 uv:TEXCOORD0;
            float4 T2W0:TEXCOORD1;
            float4 T2W1:TEXCOORD2;
            float4 T2W2:TEXCOORD3;

             //阴影三剑客之一
            SHADOW_COORDS(4) // Defined in AutoLight.cginc
            UNITY_FOG_COORDS(5) // Defined in UnityCG.cginc
        };

        v2f vert(a2v v)
        {
            v2f o;
            UNITY_INITIALIZE_OUTPUT(v2f,o);
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.texcoord,_AlbedoTex);

            float3 worldPos  = mul(unity_ObjectToWorld,v.vertex).xyz;
            float3 worldNormal = UnityObjectToWorldNormal(v.normal);
            float3 worldTagent = UnityObjectToWorldDir(v.tangent.xyz);
            float3 worldBinormal = cross(worldNormal,worldTagent) * v.tangent.w;

            //构建从切线空间到世界空间的转换矩阵
            //上面构建了切线空间在世界空间下的表示
            //那么从切线空间到世界空间的矩阵为:
            //[tangent.x,binormal.x,normal.x]
            //[tangent.y,binormal.y,normal.y]
            //[tangent.z,binormal.z,normal.z]
            //即[X,Y,Z],xyz是矢量坐标轴表示
            o.T2W0 = float4(worldTagent.x,worldBinormal.x,worldNormal.x,worldPos.x);
            o.T2W1 = float4(worldTagent.y,worldBinormal.y,worldNormal.y,worldPos.y);
            o.T2W2 = float4(worldTagent.z,worldBinormal.z,worldNormal.z,worldPos.z);

               //阴影三剑客之一
               TRANSFER_SHADOW(o); // Defined in AutoLight.cginc
               //We need this for fog rendering
               UNITY_TRANSFER_FOG(o, o.pos); // Defined in UnityCG.cginc
            return o;
        }

        half4 frag(v2f i):SV_Target
        {
            half4 specularGloss = tex2D(_SpecularTex,i.uv);
            specularGloss.a *= _Glossiness; //高光工作流中高光贴图的a通道作为粗糙度参与了运算
            half3 specularColor = specularGloss.rgb * _SpecularCol.rgb;
            half roughness = 1- specularGloss.a; //粗糙度

            //单颜色能量守恒,使用镜面高光颜色的最强分量来减少反射率
            half oneMinusReflectivity = 1 - max(max(specularColor.r,specularColor.g),specularColor.b);
            half3 diffuseColor = tex2D(_AlbedoTex,i.uv).rgb * _Albedo.rgb * oneMinusReflectivity; 

            //法线贴图
            half3 normalTagent = UnpackNormal(tex2D(_BumpMap,i.uv));
            normalTagent.xy *= _BumpScale;
            normalTagent.z = sqrt(1.0-saturate(dot(normalTagent.xy,normalTagent.xy)));

            //将切线空间的法线转换到世界空间下
            half3 worldNormal = normalize(half3(dot(i.T2W0.xyz,normalTagent),dot(i.T2W1.xyz,normalTagent),dot(i.T2W2.xyz,normalTagent)));
            float3 worldPos = float3(i.T2W0.w,i.T2W1.w,i.T2W2.w);
            half3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
            half3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
            //根据入射光方向向量 I ,和顶点法向量 N ,计算反射光方向向量。其中 I 和 N 必须被归一化,需要非常注意的是,这个 I 是指向顶点的
            half3 worldRelectDir = reflect(-worldViewDir,worldNormal); 

            //这里认为衰减系数是1
             //计算阴影和光照衰减值atten
             UNITY_LIGHT_ATTENUATION(atten, i, worldPos);

            //半角向量
            half3 halfDir = normalize(worldLightDir + worldViewDir);
            
            half NdotV = SaturateAndDot(worldNormal,worldViewDir);
            half NdotL = SaturateAndDot(worldNormal,worldLightDir);
            half NdotH = SaturateAndDot(worldNormal,halfDir);
            half LdotV = SaturateAndDot(worldLightDir,worldViewDir);
            half LdotH = SaturateAndDot(worldLightDir,halfDir);
           
            
            //Disneys的BRDF漫反射项
            half3 disneyDiffuseTerm = DisneyDiffuseBRDF(diffuseColor,NdotL,NdotV,LdotH,roughness);

            //高光反射项 F * G * D
            //菲涅尔
            half3 F = FresnelSchlick(specularColor,LdotH);
            //可见性概率
            half G = SmithJointGGX(NdotL,NdotV,roughness);
            //法线分布
            half D = GGX(NdotH,roughness * roughness);
            half3 specularTerm = F * G * D;
            
            //自发光项
            half3 emissionColorTerm = tex2D(_EmissionMap,i.uv).rgb * _EmissionColor.rgb;

            //环境光IBL
            //环境光Mipmap的LOD和粗糙度有关,因为越粗糙,反射的越模糊
            //而粗糙度和LOD又不是线性关系,Unity使用了下面的方式进行了转换
            half mipRoughness = roughness * (1.7 - 0.7 * roughness);
            //乘6后转到LOD
            half mip = mipRoughness * 6;
            half4 envMap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0,worldRelectDir,mip);

            //这两项是啥意思????
            half grazingTerm = saturate((1 - roughness) + (1 - oneMinusReflectivity));
            half surfaceReduction = 1.0 / (roughness * roughness + 1.0);

            //计算间接反射光
            half3 indirectSpecular = surfaceReduction * envMap.rgb * CustomFresnelLerp(specularColor,grazingTerm,NdotV);

            //最终的颜色 直接光+间接光
            half3 finalCol = emissionColorTerm + UNITY_PI * (disneyDiffuseTerm + specularTerm) * _LightColor0.rgb * NdotL * atten + indirectSpecular;
            UNITY_APPLY_FOG(i.fogCoord, c.rgb);
            return half4(finalCol,1);
        }
        ENDCG

        Pass
        {
            Tags { "LightMode" = "ForwardBase" }
            CGPROGRAM
            #pragma target 3.0
            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
    }
}

image.png

从左到右依次为标准金属工作流、高光工作流、简略实现的高光工作流。
其中有几项抄自原作者“改版后的第十八章”,如IBL相关计算,掠射角颜色计算

你可能感兴趣的:(【Unity Shader入门精要】基学习于物理的渲染(四))