使用资产

通过网盘分享的文件:PBR测试内容 链接: https://pan.baidu.com/s/1MpDbI4TXPY2lKaz2uZC3_w?pwd=2psd 提取码: 2psd

完整代码

Shader "Unlit/PBRShader"
{
    properties
    {
        _MainTex("MainTex", 2D) = "white"{}

        _NormalMap("NormalMap", 2D) = "bump"{}
        _NormalMapScale("NormalMapScale", Range(0,5)) = 1

        _RoughnessTex("RoughnessTex", 2D)= "white"{}
        _MetallicTex("MetallicTex", 2D)= "white"{}
        _HeightTex("HeightTex", 2D)= "white"{}
        _AOTex("AOTex", 2D)= "white"{}
        _CubeMap("CubeMap", cube)= "white"{}

        _SpecularColor("SpecularColor",color) = (1,1,1,1)
        _SpecularIntensity("SpecularIntensity", Range(0,50)) = 1
        _SpecularShininess("SpecularShininess", Range(0,10)) = 2

        [Header(Roughness Adjustment)]
        [Space]
        _RoughnessIntensity ("RoughnessIntensity", Range(0, 2)) = 1
        [Space]
        _RoughnessBias ("Bias", Range(-1.0, 1.0)) = 0
        [Space]
        _RoughnessRemapMin ("Remap Min", Range(0, 1)) = 0
        _RoughnessRemapMax ("Remap Max", Range(0, 1)) = 1
        [Header(ParallaxMapping Basic)]
        _ParallaxScale ("Parallax Scale 未使用", Range(0, 10)) = 0.1
        [Header(ParallaxOcclusionMapping)]
        _HeightScale ("Height Scale", Range(0, 0.2)) = 0.05
        _MinLayers("Parallax Min Layers", Range(1, 128)) = 10
        _MaxLayers("Parallax Max Layers", Range(1, 128)) = 50
        _HeightReferencePlane ("Height Reference Plane", Range(0, 1)) = 0.5  // 解决凹凸问题
        //_POMFadeDistance ("POM Near Fade Distance", Range(0, 10)) = 2.0  
        _ShadowSoftness("POM Shadow Strength" ,Range(0, 10)) = 1.0
        _ShadowSteps("POM Shadow Steps" ,Range(0, 16)) = 8

        [Header(EnvBRDFLUT)]
        [Toggle]_UseCustomLUT("Custom BRDF LUT", float) = 1.0
        _CustomBRDFLUT("Custom BRDF LUT", 2D) = "white" {}

    }

    Subshader
    {
        Tags
        {
            "RenderType"="Opaque"
            "Queue"="Geometry"
        }

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

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
             
            struct appdata
            {
                float4 vertex   : POSITION;
                float3 normal   : NORMAL;
                float4 tangent  : TANGENT;
                float2 uv       : TEXCOORD0;
                
            };

            struct v2f
            {
                float4 pos          : SV_POSITION;
                float4 positionWS   : TEXCOORD0;
                float3 normalWS     : TEXCOORD1;
                float3 tangentWS    : TEXCOORD2;
                float3 bitangentWS  : TEXCOORD3;
                float2 texcoord     : TEXCOORD4;
                float3 viewDirTS    : TEXCOORD5;
                float3 lightDirTS   : TEXCOORD6;
                UNITY_SHADOW_COORDS(7)
            };

            sampler2D _MainTex;
            half4 _MainTex_ST;
            sampler2D _NormalMap;
            sampler2D _RoughnessTex;
            sampler2D _MetallicTex;
            sampler2D _HeightTex;
            sampler2D _AOTex;
            samplerCUBE _CubeMap;
            float4 _CubeMap_HDR; //此参数声明后,Unity会自动配置
            half _NormalMapScale;
            half4 _SpecularColor;
            half _SpecularIntensity;
            half _SpecularShininess;
           

            half _RoughnessIntensity;
            half _RoughnessBias;
            half _RoughnessRemapMin;
            half _RoughnessRemapMax;
            half _RoughnessBoost;

            half _ParallaxScale;

            half _HeightScale;
            half _MinLayers;
            half _MaxLayers;
            half _HeightReferencePlane;
            half _POMFadeDistance;

            half _ShadowSoftness;
            half _ShadowSteps;

            half _UseCustomLUT;
            sampler2D _CustomBRDFLUT;

            float3 ParallaxOcclusionMapping(float2 uv, float3 viewDirTS, float3 lightDirTS)
            {
                
                // === 保存原始UV导数 ===
                float2 dx = ddx(uv);
                float2 dy = ddy(uv);
                
                // 这个系数能保证在极低角度时视差平滑消失
                //float angleFade = saturate(viewDirTS.z * 5.0);
                float angleFade = saturate(pow(viewDirTS.z, 0.5));
                //float angleFade = smoothstep(0.15, 0.4, viewDirTS.z);

                // 截断淡出,效果较差
                // float2 rawOffset = viewDirTS.xy / max(viewDirTS.z, 0.0001) * _HeightScale;
                // float maxLen = _HeightScale * 2.0; 
                // float currentLen = length(rawOffset);
                // float2 maxParallaxOffset = rawOffset * (min(currentLen, maxLen) / max(currentLen, 0.001));

                // === 1. 动态调整采样层数 ===
                float numLayers = lerp(_MaxLayers, _MinLayers, abs(viewDirTS.z));
                // === 2. 计算步进参数 ===
                float layerStepDistance = 1.0 / numLayers;              // 步进距离
                float2 maxParallaxOffset = viewDirTS.xy / max(viewDirTS.z, 0.001) * _HeightScale * angleFade; //最大步进距离
                float2 deltaUV = maxParallaxOffset / numLayers; //步进UV
                // === 3. 初始化 第一层数据===
                float currentLayerDistance = 0.0;         //起始层基准
                float2 currentUV = uv + maxParallaxOffset * (1.0 - _HeightReferencePlane);   // 当前UV坐标
                //float currentHeight = 1 - tex2D(_HeightTex, currentUV).r; // 初始表面高度
                float currentHeight = 1.0 - tex2Dgrad(_HeightTex, currentUV, dx, dy).r;

                // 缓存上一步的数据,避免循环结束后重复采样
                float prevHeight = currentHeight;
                float prevLayerDistance = 0.0;

                //[unroll(50)] // 展开循环优化(数字要≥_MaxLayers)
                [loop] //标记循环
                for(int i = 0; i < numLayers; i++)
                {
                    // 1.检查是否当前位置数值是否大于当前高度值
                    if(currentLayerDistance >= currentHeight)
                        break;
                    
                    // 2.记录旧状态
                    prevHeight = currentHeight;
                    prevLayerDistance = currentLayerDistance;

                    // 3.步进操作
                    // 沿视线方向步进,下一层的数据,这里虽然命名是current,但是数据已经是下一层的了
                    currentUV -= deltaUV;                               //UV步进1层
                    currentLayerDistance +=  layerStepDistance;         //当前位置步进1层 

                    // 4.采样新高度
                    //currentHeight = 1 - tex2Dlod(_HeightTex, float4(currentUV, 0, 0)).r; //深度值随UV步进1层
                    currentHeight = 1.0 - tex2Dgrad(_HeightTex, currentUV, dx, dy).r;
                }
                // 步进操作前,也就是当前UV
                float2 prevUV = currentUV + deltaUV;

                //当前步进距离对高度值影响              
                float beforeDistance = prevHeight - prevLayerDistance;
                 
                //下一层,总步进距离对高度值影响
                float afterDistance = currentHeight - currentLayerDistance;  
    
                // 插值权重,两个端点之间的线性插值
                float weight = afterDistance / (afterDistance - beforeDistance);

                // 最终UV = 加权平均
                // 对每一次步进的UV插值
                float2 finalUV = lerp(currentUV, prevUV, weight);
                //float finalHeight = lerp(currentLayerDistance, prevLayerDistance, weight);

                // === 自阴影循环 (Soft Shadows) ===
                float shadow = 1.0;
                
                //float lightAngleFade = smoothstep(0.05, 0.25, lightDirTS.z);
                float lightAngleFade = saturate(pow(lightDirTS.z, 0.5));
                
                if(_ShadowSoftness > 0.0 && angleFade > 0.01 && lightDirTS.z > 0.0)
                {
                    float hitDepth = 1.0 - tex2Dgrad(_HeightTex, finalUV, dx, dy).r;
                    
                    if(hitDepth > 0.001)
                    {
                        // 计算完整的UV偏移方向(从交点到光源方向在UV上的投影)
                        float2 lightDirUV = lightDirTS.xy / max(lightDirTS.z, 0.001) * _HeightScale;
                        
                        // 限制最大追踪距离,防止低角度时偏移过大
                        float maxOffset = _HeightScale * 2.0;
                        float offsetLen = length(lightDirUV);
                        if(offsetLen > maxOffset)
                        {
                            lightDirUV = lightDirUV / offsetLen * maxOffset;
                        }
                        
                        // 按深度比例的总UV偏移
                        float2 totalUV = lightDirUV * hitDepth;
                        
                        // 均匀分配到每一步
                        float2 stepUV = totalUV / _ShadowSteps;
                        float stepDepth = hitDepth / _ShadowSteps;
                        
                        float currentDepth = hitDepth;
                        float2 currentUV = finalUV;
                        
                        [loop]
                        for(int j = 0; j < _ShadowSteps; j++)
                        {
                            currentDepth -= stepDepth;
                            currentUV += stepUV;
                            
                            if(currentDepth <= 0.0)
                                break;
                            
                            float terrainDepth = 1.0 - tex2Dgrad(_HeightTex, currentUV, dx, dy).r;
                            
                            if(terrainDepth < currentDepth)
                            {
                                float occlusion = currentDepth - terrainDepth;
                                shadow = min(shadow, 1.0 - saturate(occlusion * _ShadowSoftness));
                                
                                if(shadow < 0.01)
                                {
                                    shadow = 0.0;
                                    break;
                                }
                            }
                        }
                    }
                }
                // 根据淡出系数混合阴影
                shadow = lerp(1.0, shadow, angleFade * lightAngleFade);
                
                // 根据淡出系数混合阴影(让阴影也随视角淡出)
                //shadow = lerp(1.0, shadow, angleFade);

                //return finalUV;
                return float3(finalUV,shadow);
            }

            float2 EnvBRDFApprox(float roughness, float NoV)
            {
                // Epic Games通过拟合大量数据得出的魔法系数
                const float4 c0 = float4(-1.0, -0.0275, -0.572, 0.022);
                const float4 c1 = float4(1.0, 0.0425, 1.04, -0.04);

                float4 r = roughness * c0 + c1;
                float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
                float2 AB = float2(-1.04, 1.04) * a004 + r.zw;

                return AB;  // AB.x = scale, AB.y = bias
            }

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.positionWS = mul(unity_ObjectToWorld, v.vertex);

                o.normalWS = UnityObjectToWorldNormal(v.normal);
                o.tangentWS = UnityObjectToWorldDir(v.tangent.xyz);
                //* v.tangent.w处理平台差异
                o.bitangentWS = cross(o.normalWS, o.tangentWS) * v.tangent.w;
                o.texcoord = TRANSFORM_TEX(v.uv,_MainTex);

                //ParallaxMapping_Basic start
                float3x3 TBN = float3x3(o.tangentWS, o.bitangentWS, o.normalWS);
                float3 worldViewDir = normalize(_WorldSpaceCameraPos - o.positionWS);       
                o.viewDirTS = mul(TBN, worldViewDir); 

                half3 lightWS = normalize(_WorldSpaceLightPos0.xyz);
                o.lightDirTS= mul(TBN, lightWS); 

                //ParallaxMapping_Basic end
                TRANSFER_SHADOW(o);
                return o;
            }
            

            float4 frag(v2f i) : SV_Target
            {
                //vectors
                float3 positionWS = i.positionWS; 
                float3 normalWS = normalize(i.normalWS);
                float3 tangentWS = normalize(i.tangentWS);
                float3 bitangentWS = normalize(i.bitangentWS);
                float3x3 TBN = float3x3(tangentWS, bitangentWS, normalWS);
                half3 lightWS = normalize(_WorldSpaceLightPos0.xyz);
                float3 viewDir = normalize(_WorldSpaceCameraPos - positionWS);

                half3 lightDirTS = i.lightDirTS; 
                //************tex*************
                //ParallaxMapping_Basic start
                //float height = 1.0 - tex2D(_HeightTex, i.texcoord).r;
                //float2 offset = i.viewDirTS.xy / i.viewDirTS.z * height * _ParallaxScale * 0.01;
                //float2 texcoord = i.texcoord - offset;
                //ParallaxMapping_Basic end

                float3 POMData = ParallaxOcclusionMapping(i.texcoord, i.viewDirTS,  lightDirTS);
                float2 texcoord = POMData.xy;
                float  POMShadow = POMData.z;

                float3 lightColor = _LightColor0.xyz;
                float4 baseCol = tex2D(_MainTex, texcoord);
                float3 normalMapData = UnpackNormal(tex2D(_NormalMap, texcoord)).xyz;
                normalMapData.xy *= _NormalMapScale;
                float metallic = tex2D(_MetallicTex, texcoord).b;
                float roughness = tex2D(_RoughnessTex, texcoord).r + _RoughnessBoost;

                //halfLambet
                float3 pixelNormal = normalize(mul(normalMapData, TBN));
                float nol = max(0, dot(pixelNormal, lightWS));
                float halfLambet = nol*0.5 + 0.5;
                float3 diffuseColor = baseCol * nol * lightColor;
                //Specular
                //phong
                half3 reflectDir = reflect(-lightWS, pixelNormal);
                half phongItem = max(0, dot(reflectDir, viewDir));
               
                //Blinn-phong
                half3 halfDir = normalize(lightWS + viewDir);
                half blinnPhongItem = max(0, dot(pixelNormal, halfDir));       

               
              
                //整体漫反射剔除金属的的漫反射(错误写法,能量不守恒)
                //float3 pbrDiffuseColor = lerp(0.96 * diffuseColor, 0, metallic);
                
                roughness = roughness * _RoughnessIntensity + _RoughnessBias;
                roughness = clamp(roughness, _RoughnessRemapMin, _RoughnessRemapMax);

                //GGXSpecular
                float3 f0 = lerp(float3(0.04,0.04,0.04), baseCol, metallic);
                float roughness2 = roughness * roughness;
                float d = roughness2*roughness2/(3.14159*(blinnPhongItem * blinnPhongItem * (roughness2 * roughness2 - 1) + 1)*(blinnPhongItem * blinnPhongItem * (roughness2 * roughness2 - 1) + 1));

                float k = (roughness + 1) * (roughness + 1) / 8;
                float nov = max(0, dot(pixelNormal, viewDir));
                float gv = nov / (nov * (1 - k) + k);
                float gl = nol / (nol * (1 - k) + k);
                float g = gv * gl; 
                float3 f = f0 + (1-f0) * pow(1 - max(0,(dot(viewDir, halfDir))), 5);
                float3 KD_GGX =  (1 - f)* (1 - metallic);
                float3 ggxBRDF = d * f * g / (4 * nol * nov  + 0.001);
                float3 pbrSpecularColor = ggxBRDF * _SpecularColor * lightColor * nol;
                float3 pbrDiffuseColor =  baseCol /3.14159 * KD_GGX * nol * lightColor;


                //******环境光处理**********

                /******************漫反射环境光*********************/
                //EnvironmentColor
                //1.环境光(Unity 自动处理)
                fixed3 envColor = UNITY_LIGHTMODEL_AMBIENT.rgb * baseCol.rgb;
                //               ^^^^^^^^^^^^^^^^^^^^^^^ Unity 内置的环境光颜色
                //2.SH环境光(支持 Skybox/Gradient/Color)
                fixed3 envColorSH = ShadeSH9(half4(normalWS, 1.0)) * baseCol.rgb;
                //               ^^^^^^^ Unity 的球谐函数,根据法线方向采样环境光
                /******************镜面反射环境光*********************/
                //1.采样反射探针(Reflection Probe)
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectDir);
                half3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR);
                    
                // 计算反射方向
                float3 reflviewDir = reflect(-viewDir, normalize(pixelNormal));
               
                //环境光漫反射
                float3 f_indirect = f0 + (1-f0) * pow(1 - max(0,(dot(viewDir, pixelNormal))), 5);
                float3 kD_indirect  = (1.0 - f_indirect) * (1.0 - metallic);
                float3 indirectDiffuse  = kD_indirect * envColorSH;
                
                //环境光镜面漫反射
                float mipLevel = roughness* 7.0;
                float4 envSpecularRaw = texCUBElod(_CubeMap, float4(reflviewDir, mipLevel));
                half3 envSpecular = DecodeHDR(envSpecularRaw, _CubeMap_HDR); 

                //1.拟合BRDF(未使用)
                float2 envBRDF = EnvBRDFApprox(roughness, nov);
                float3 envSpecularEasy = envSpecular * f_indirect;
                //2.采样BRDFLUT
                float2 brdfLUT = tex2D(unity_NHxRoughness, float2(roughness, nov)).rg;
                float2 CustombrdfLUT = tex2D(_CustomBRDFLUT, float2(roughness, nov)).rg;
                
                if(_UseCustomLUT > 0.5)
                {
                    brdfLUT = CustombrdfLUT;
                }
                envSpecular =  envSpecular * (f0 * brdfLUT.x + brdfLUT.y);
                
                //AO
                float AO = tex2D(_AOTex, texcoord).r;
                //float fakeAO = saturate(dot(normalWS, float3(0,1,0))) * 0.6 + 0.4;



                //Shadow
                float shadow = SHADOW_ATTENUATION(i);
 
                //output
                float3 finalColor = (pbrDiffuseColor + pbrSpecularColor) * shadow + (indirectDiffuse + envSpecular )* AO * POMShadow ;


                return float4(finalColor, 1); 
            }
            ENDCG
        }
        
    }FallBack"Diffuse"

}

测试大致效果