【Unity Shader入門(mén)精要】基學(xué)習(xí)于物理的渲染(四)

基于物理的渲染四(PBS)

簡(jiǎn)略實(shí)現(xiàn)PBS

Shader "Unlit/SelfPBR"
{
    Properties
    {
        //漫反射材質(zhì)控制顏色
        _Albedo("Color",Color)=(1,1,1,1)
        //漫反射紋理
        _AlbedoTex("Albedo",2D)="white"{}

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

        //高光反射材質(zhì)控制顏色
        _SpecularCol("Specular Color",Color)=(0.2, 0.2, 0.2)
        //高光反射紋理
        _SpecularTex("Specular Tex",2D)="white"{}

        //自發(fā)光控制顏色
        _EmissionColor("Emission Color",Color)=(0,0,0)
        //自發(fā)光紋理
        _EmissionMap("Emission Map",2D)="white"{}

       //法線(xiàn)控制項(xiàng)
        _BumpScale("Bump Scale",float)=1.0
        //法線(xiàn)紋理
        _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);
        }

        //法線(xiàn)分布函數(shù)
        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);
        }
        
        //陰影遮擋函數(shù)
        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;

            //構(gòu)建從切線(xiàn)空間到世界空間的轉(zhuǎn)換矩陣
            //上面構(gòu)建了切線(xiàn)空間在世界空間下的表示
            //那么從切線(xiàn)空間到世界空間的矩陣為:
            //[tangent.x,binormal.x,normal.x]
            //[tangent.y,binormal.y,normal.y]
            //[tangent.z,binormal.z,normal.z]
            //即[X,Y,Z],xyz是矢量坐標(biāo)軸表示
            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通道作為粗糙度參與了運(yùn)算
            half3 specularColor = specularGloss.rgb * _SpecularCol.rgb;
            half roughness = 1- specularGloss.a; //粗糙度

            //單顏色能量守恒农猬,使用鏡面高光顏色的最強(qiáng)分量來(lái)減少反射率
            half oneMinusReflectivity = 1 - max(max(specularColor.r,specularColor.g),specularColor.b);
            half3 diffuseColor = tex2D(_AlbedoTex,i.uv).rgb * _Albedo.rgb * oneMinusReflectivity; 

            //法線(xiàn)貼圖
            half3 normalTagent = UnpackNormal(tex2D(_BumpMap,i.uv));
            normalTagent.xy *= _BumpScale;
            normalTagent.z = sqrt(1.0-saturate(dot(normalTagent.xy,normalTagent.xy)));

            //將切線(xiàn)空間的法線(xiàn)轉(zhuǎn)換到世界空間下
            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));
            //根據(jù)入射光方向向量 I 却音,和頂點(diǎn)法向量 N 改抡,計(jì)算反射光方向向量。其中 I 和 N 必須被歸一化系瓢,需要非常注意的是阿纤,這個(gè) I 是指向頂點(diǎn)的
            half3 worldRelectDir = reflect(-worldViewDir,worldNormal); 

            //這里認(rèn)為衰減系數(shù)是1
             //計(jì)算陰影和光照衰減值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漫反射項(xiàng)
            half3 disneyDiffuseTerm = DisneyDiffuseBRDF(diffuseColor,NdotL,NdotV,LdotH,roughness);

            //高光反射項(xiàng) F * G * D
            //菲涅爾
            half3 F = FresnelSchlick(specularColor,LdotH);
            //可見(jiàn)性概率
            half G = SmithJointGGX(NdotL,NdotV,roughness);
            //法線(xiàn)分布
            half D = GGX(NdotH,roughness * roughness);
            half3 specularTerm = F * G * D;
            
            //自發(fā)光項(xiàng)
            half3 emissionColorTerm = tex2D(_EmissionMap,i.uv).rgb * _EmissionColor.rgb;

            //環(huán)境光IBL
            //環(huán)境光Mipmap的LOD和粗糙度有關(guān),因?yàn)樵酱植谝穆瓷涞脑侥:?            //而粗糙度和LOD又不是線(xiàn)性關(guān)系欠拾,Unity使用了下面的方式進(jìn)行了轉(zhuǎn)換
            half mipRoughness = roughness * (1.7 - 0.7 * roughness);
            //乘6后轉(zhuǎn)到LOD
            half mip = mipRoughness * 6;
            half4 envMap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0,worldRelectDir,mip);

            //這兩項(xiàng)是啥意思?骗绕?藐窄??
            half grazingTerm = saturate((1 - roughness) + (1 - oneMinusReflectivity));
            half surfaceReduction = 1.0 / (roughness * roughness + 1.0);

            //計(jì)算間接反射光
            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

從左到右依次為標(biāo)準(zhǔn)金屬工作流爹谭、高光工作流枷邪、簡(jiǎn)略實(shí)現(xiàn)的高光工作流榛搔。
其中有幾項(xiàng)抄自原作者“改版后的第十八章”诺凡,如IBL相關(guān)計(jì)算,掠射角顏色計(jì)算

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末践惑,一起剝皮案震驚了整個(gè)濱河市腹泌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尔觉,老刑警劉巖凉袱,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡专甩,警方通過(guò)查閱死者的電腦和手機(jī)钟鸵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涤躲,“玉大人棺耍,你說(shuō)我怎么就攤上這事≈钟#” “怎么了蒙袍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嫩挤。 經(jīng)常有香客問(wèn)我害幅,道長(zhǎng),這世上最難降的妖魔是什么岂昭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任以现,我火速辦了婚禮,結(jié)果婚禮上佩抹,老公的妹妹穿的比我還像新娘叼风。我一直安慰自己,他們只是感情好棍苹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布无宿。 她就那樣靜靜地躺著,像睡著了一般枢里。 火紅的嫁衣襯著肌膚如雪孽鸡。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天栏豺,我揣著相機(jī)與錄音彬碱,去河邊找鬼。 笑死奥洼,一個(gè)胖子當(dāng)著我的面吹牛巷疼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灵奖,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嚼沿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了瓷患?” 一聲冷哼從身側(cè)響起骡尽,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎擅编,沒(méi)想到半個(gè)月后攀细,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體箫踩,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年谭贪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了境钟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡俭识,死狀恐怖吱韭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鱼的,我是刑警寧澤理盆,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凑阶,受9級(jí)特大地震影響猿规,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宙橱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一姨俩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧师郑,春花似錦环葵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至地梨,卻和暖如春菊卷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宝剖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工洁闰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人万细。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓扑眉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赖钞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腰素,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355