candycat1992 / Unity_Shaders_Book

:book: 书籍《Unity Shader入门精要》源代码
5.47k stars 1.61k forks source link

改版后18章PBR 18.2的一些纠错 以及提供缺失的shader代码 #312

Open metacarbon opened 2 years ago

metacarbon commented 2 years ago

引擎版本为Unity5.2和第一版保持一致,不排除一些修正在新版本里不需要

pg.359 参考pg.354法线分布函数以及Unity源文件中的实现: CustomGGXTerm函数中 变量a (Alpha) 应该为 roughness的平方,a2 应该为 a 的平方,也就是roughness的4次方

pg.360 IBL对环境光采样后 变量half4 envMap为HDR值RGBM,直接使用会导致材质过曝,因此添加了DecodeHDR函数将HDR转换为RGB,可以参考 https://catlikecoding.com/unity/tutorials/rendering/part-8/Rendering-8.pdf

Unity5.2版本中没有定义UNITY_INV_PI,添加了宏定义

Properties中的_SpecColor变量名会产生命名冲突,所以改成了_SpecularColor

效果图: pbr01

代码:

Shader "Unity Shaders Book/Chapter 18/Custom PBR"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _MainTex ("Albedo", 2D) = "white" {}
        _Glossiness ("Smoothess", Range(0.0, 1.0)) = 0.5
        _SpecularColor ("Specular", Color) = (0.2, 0.2, 0.2)
        _SpecGlossMap ("Specular (RGB) Smoothness (A)", 2D) = "white" {}
        _BumpScale ("Bump Scale", Float) = 1.0
        _BumpMap ("Normal Map", 2D) = "bump" {}
        _EmissionColor ("Color", Color) = (0, 0, 0)
        _EmissionMap ("Emission", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 300

        Pass
        {
            Tags { "LightMode"="ForwardBase" }

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

            #pragma target 3.0

            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog

            #pragma vertex vert
            #pragma fragment frag

            #define UNITY_INV_PI 0.31830988618f

            fixed4 _Color;
            sampler2D _MainTex;
            fixed _Glossiness;
            fixed4 _SpecularColor;
            sampler2D _SpecGlossMap;
            float _BumpScale;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            fixed4 _EmissionColor;
            sampler2D _EmissionMap;

            float4 _MainTex_ST;
            float4 _SpecGlossMap_ST;
            float4 _EmissionMap_ST;

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

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 TtoW0 : TEXCOORD1;
                float4 TtoW1 : TEXCOORD2;
                float4 TtoW2 : 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);    //Defined in HLSLSupport.cginc

                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);     //Defined in UnityCG.cginc

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

                o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
                o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
                o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  

                //We need this for shadow receving
                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;

            }

            inline half3 CustomDisneyDiffuseTerm(half NdotV, half NdotL, half LdotH, half roughness, half3 baseColor) {
                half fd90 = 0.5 + 2 * LdotH * LdotH * roughness;

                //Two schlick fresnel term
                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 half CustomSmithJointGGXVisibilityTerm(half NdotL, half NdotV, half roughness) {
                //Original formulation
                //lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
                //lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
                //G = 1 / (1 + lambda_v + lambda_l);

                //Approximation of the above (simplify the sqrt, not mathematically correct but close enough)
                half a = roughness * roughness;
                half lambdaV = NdotL * (NdotV * (1 - a) + a);
                half lambdaL = NdotV * (NdotL * (1 - a) + a);

                return 0.5f / (lambdaV + lambdaL + 1e-5f);

            }

            inline half CustomGGXTerm(half NdotH, half roughness) {
                half a = roughness * roughness;
                half a2 = a * a;
                half d = (a2 - 1.0f) * NdotH * NdotH + 1.0f;
                return UNITY_INV_PI * a2 / (d * d + 1e-7f);
            }

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

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

            half4 frag(v2f i) : SV_Target {
                //Prepare all the inputs
                half4 specGloss = tex2D(_SpecGlossMap, i.uv);
                specGloss.a *= _Glossiness;
                half3 specColor = specGloss.rgb * _SpecularColor.rgb;
                half roughness = 1 - specGloss.a;

                half oneMinusReflectivity = 1 - max(max(specColor.r, specColor.g), specColor.b);

                half3 diffColor = _Color.rgb * tex2D(_MainTex, i.uv).rgb * oneMinusReflectivity;

                half3 normalTangent = UnpackNormal(tex2D(_BumpMap, i.uv));
                normalTangent.xy *= _BumpScale;
                normalTangent.z = sqrt(1.0 - saturate(dot(normalTangent.xy, normalTangent.xy)));
                half3 normalWorld = normalize(half3(dot(i.TtoW0.xyz, normalTangent), dot(i.TtoW1.xyz, normalTangent), dot(i.TtoW2.xyz, normalTangent)));

                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                half3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));      //Defined in UnityCG.cginc
                half3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));        //Defined in UnityCG.cginc

                half3 reflDir = reflect(-viewDir, normalWorld);

                UNITY_LIGHT_ATTENUATION(atten, i, worldPos);        //Defined in AutoLight.cginc

                //Compute BRDF terms
                half3 halfDir = normalize(lightDir + viewDir);
                half nv = saturate(dot(normalWorld, viewDir));
                half nl = saturate(dot(normalWorld, lightDir));
                half nh = saturate(dot(normalWorld, halfDir));
                half lv = saturate(dot(lightDir, viewDir));
                half lh = saturate(dot(lightDir, halfDir));

                //Diffuse term
                half3 diffuseTerm = CustomDisneyDiffuseTerm(nv, nl, lh, roughness, diffColor);

                //Specular term
                half V = CustomSmithJointGGXVisibilityTerm(nl, nv, roughness);
                half D = CustomGGXTerm(nh, roughness * roughness);
                half3 F = CustomFresnelTerm(specColor, lh);
                half3 specularTerm = F * V * D;

                //Emission term
                half3 emisstionTerm = tex2D(_EmissionMap, i.uv).rgb * _EmissionColor.rgb;

                //IBL
                half perceptualRoughness = roughness * (1.7 - 0.7 * roughness);
                half mip = perceptualRoughness * 6;
                half4 envMap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflDir, mip);     //Defined in HLSLSupport.cginc
                //Decode the 4 channels HDR data to RGB format. Otherwise the indirectLight will be too bright because the cube
                //map contains high dynamic range colors, which allows the values greater than 1.
                half3 decodeEnvMap = DecodeHDR(envMap, unity_SpecCube0_HDR);    //Defined in UnityCG.cginc
                half grazingTerm = saturate((1 - roughness) + (1 - oneMinusReflectivity));
                half surfaceReduction = 1.0 / (roughness * roughness + 1.0);
                half3 indirectSpecular = surfaceReduction * decodeEnvMap.rgb * CustomFresnelLerp(specColor, grazingTerm, nv);

                //Combine all togather
                half3 col = emisstionTerm + UNITY_PI * (diffuseTerm + specularTerm) * _LightColor0.rgb * nl * atten + indirectSpecular;

                UNITY_APPLY_FOG(i.fogCoord, c.rgb);     //Defined in UnityCG.cginc

                return half4(col, 1);
            }

            ENDCG
        }
    }
    FallBack Off
}
CatDroid commented 2 years ago

对 _SpecColor unity 2020.3 加入了 Lighting.cginc 头文件后就会redefined

CatDroid commented 2 years ago

引擎版本为Unity5.2和第一版保持一致,不排除一些修正在新版本里不需要

pg.359 参考pg.354法线分布函数以及Unity源文件中的实现: CustomGGXTerm函数中 变量a (Alpha) 应该为 roughness的平方,a2 应该为 a 的平方,也就是roughness的4次方

pg.360 IBL对环境光采样后 变量half4 envMap为HDR值RGBM,直接使用会导致材质过曝,因此添加了DecodeHDR函数将HDR转换为RGB,可以参考 https://catlikecoding.com/unity/tutorials/rendering/part-8/Rendering-8.pdf

Unity5.2版本中没有定义UNITY_INV_PI,添加了宏定义

Properties中的_SpecColor变量名会产生命名冲突,所以改成了_SpecularColor

效果图: pbr01

代码:

Shader "Unity Shaders Book/Chapter 18/Custom PBR"
{
  Properties
  {
      _Color ("Color", Color) = (1, 1, 1, 1)
      _MainTex ("Albedo", 2D) = "white" {}
      _Glossiness ("Smoothess", Range(0.0, 1.0)) = 0.5
      _SpecularColor ("Specular", Color) = (0.2, 0.2, 0.2)
      _SpecGlossMap ("Specular (RGB) Smoothness (A)", 2D) = "white" {}
      _BumpScale ("Bump Scale", Float) = 1.0
      _BumpMap ("Normal Map", 2D) = "bump" {}
      _EmissionColor ("Color", Color) = (0, 0, 0)
      _EmissionMap ("Emission", 2D) = "white" {}
  }
  SubShader
  {
      Tags { "RenderType"="Opaque" }
      LOD 300

      Pass
      {
          Tags { "LightMode"="ForwardBase" }

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

          #pragma target 3.0

          #pragma multi_compile_fwdbase
          #pragma multi_compile_fog

          #pragma vertex vert
          #pragma fragment frag

          #define UNITY_INV_PI 0.31830988618f

          fixed4 _Color;
          sampler2D _MainTex;
          fixed _Glossiness;
          fixed4 _SpecularColor;
          sampler2D _SpecGlossMap;
          float _BumpScale;
          sampler2D _BumpMap;
          float4 _BumpMap_ST;
          fixed4 _EmissionColor;
          sampler2D _EmissionMap;

          float4 _MainTex_ST;
          float4 _SpecGlossMap_ST;
          float4 _EmissionMap_ST;

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

          struct v2f {
              float4 pos : SV_POSITION;
              float2 uv : TEXCOORD0;
              float4 TtoW0 : TEXCOORD1;
              float4 TtoW1 : TEXCOORD2;
              float4 TtoW2 : 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);    //Defined in HLSLSupport.cginc

              o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
              o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);     //Defined in UnityCG.cginc

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

              o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
              o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
              o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  

              //We need this for shadow receving
              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;

          }

          inline half3 CustomDisneyDiffuseTerm(half NdotV, half NdotL, half LdotH, half roughness, half3 baseColor) {
              half fd90 = 0.5 + 2 * LdotH * LdotH * roughness;

              //Two schlick fresnel term
              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 half CustomSmithJointGGXVisibilityTerm(half NdotL, half NdotV, half roughness) {
              //Original formulation
              //lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
              //lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
              //G = 1 / (1 + lambda_v + lambda_l);

              //Approximation of the above (simplify the sqrt, not mathematically correct but close enough)
              half a = roughness * roughness;
              half lambdaV = NdotL * (NdotV * (1 - a) + a);
              half lambdaL = NdotV * (NdotL * (1 - a) + a);

              return 0.5f / (lambdaV + lambdaL + 1e-5f);

          }

          inline half CustomGGXTerm(half NdotH, half roughness) {
              half a = roughness * roughness;
              half a2 = a * a;
              half d = (a2 - 1.0f) * NdotH * NdotH + 1.0f;
              return UNITY_INV_PI * a2 / (d * d + 1e-7f);
          }

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

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

          half4 frag(v2f i) : SV_Target {
              //Prepare all the inputs
              half4 specGloss = tex2D(_SpecGlossMap, i.uv);
              specGloss.a *= _Glossiness;
              half3 specColor = specGloss.rgb * _SpecularColor.rgb;
              half roughness = 1 - specGloss.a;

              half oneMinusReflectivity = 1 - max(max(specColor.r, specColor.g), specColor.b);

              half3 diffColor = _Color.rgb * tex2D(_MainTex, i.uv).rgb * oneMinusReflectivity;

              half3 normalTangent = UnpackNormal(tex2D(_BumpMap, i.uv));
              normalTangent.xy *= _BumpScale;
              normalTangent.z = sqrt(1.0 - saturate(dot(normalTangent.xy, normalTangent.xy)));
              half3 normalWorld = normalize(half3(dot(i.TtoW0.xyz, normalTangent), dot(i.TtoW1.xyz, normalTangent), dot(i.TtoW2.xyz, normalTangent)));

              float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
              half3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));      //Defined in UnityCG.cginc
              half3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));        //Defined in UnityCG.cginc

              half3 reflDir = reflect(-viewDir, normalWorld);

              UNITY_LIGHT_ATTENUATION(atten, i, worldPos);        //Defined in AutoLight.cginc

              //Compute BRDF terms
              half3 halfDir = normalize(lightDir + viewDir);
              half nv = saturate(dot(normalWorld, viewDir));
              half nl = saturate(dot(normalWorld, lightDir));
              half nh = saturate(dot(normalWorld, halfDir));
              half lv = saturate(dot(lightDir, viewDir));
              half lh = saturate(dot(lightDir, halfDir));

              //Diffuse term
              half3 diffuseTerm = CustomDisneyDiffuseTerm(nv, nl, lh, roughness, diffColor);

              //Specular term
              half V = CustomSmithJointGGXVisibilityTerm(nl, nv, roughness);
              half D = CustomGGXTerm(nh, roughness * roughness);
              half3 F = CustomFresnelTerm(specColor, lh);
              half3 specularTerm = F * V * D;

              //Emission term
              half3 emisstionTerm = tex2D(_EmissionMap, i.uv).rgb * _EmissionColor.rgb;

              //IBL
              half perceptualRoughness = roughness * (1.7 - 0.7 * roughness);
              half mip = perceptualRoughness * 6;
              half4 envMap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflDir, mip);     //Defined in HLSLSupport.cginc
              //Decode the 4 channels HDR data to RGB format. Otherwise the indirectLight will be too bright because the cube
              //map contains high dynamic range colors, which allows the values greater than 1.
              half3 decodeEnvMap = DecodeHDR(envMap, unity_SpecCube0_HDR);    //Defined in UnityCG.cginc
              half grazingTerm = saturate((1 - roughness) + (1 - oneMinusReflectivity));
              half surfaceReduction = 1.0 / (roughness * roughness + 1.0);
              half3 indirectSpecular = surfaceReduction * decodeEnvMap.rgb * CustomFresnelLerp(specColor, grazingTerm, nv);

              //Combine all togather
              half3 col = emisstionTerm + UNITY_PI * (diffuseTerm + specularTerm) * _LightColor0.rgb * nl * atten + indirectSpecular;

              UNITY_APPLY_FOG(i.fogCoord, c.rgb);     //Defined in UnityCG.cginc

              return half4(col, 1);
          }

          ENDCG
      }
  }
  FallBack Off
}

请问你有 "图18.7 使用自定义的基于物理渲染的材质 (立方体材质纹理来源 Standard Shader Gallery")“

Standard Shader Gallery 这个的来源还有吗? 应该有几张图片的