GameTech/URP 渲染实战
源码 / Ch13/PBRValidate.shader
Ch13/PBRValidate.shader
// PBR 校验视图:读取 URP Lit 材质的属性(同名),把“超出合理范围”的像素用斜条纹高亮。
//   红:电介质 Albedo 过暗      蓝:电介质 Albedo 过亮
//   黄:金属度处在 0~1 之间的灰区(通常只该出现在过渡边缘)
//   品红:金属的 Albedo(即 F0)过暗
// 用法一:在材质上临时把 Shader 换成它(同名属性与关键字会保留),看完再换回 Lit。
// 用法二:RenderObjects 的 Override Mode = Shader,一键对整个场景生效(见 PBRValidateFeature 的配置代码)。
Shader "Cookbook/Ch13/PBRValidate"
{
    Properties
    {
        [MainTexture] _BaseMap ("Albedo", 2D) = "white" {}
        [MainColor] _BaseColor ("Color", Color) = (1, 1, 1, 1)
        _Metallic ("Metallic", Range(0, 1)) = 0
        _Smoothness ("Smoothness", Range(0, 1)) = 0.5
        _MetallicGlossMap ("Metallic (R) Smoothness (A)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }
        Pass
        {
            Name "Validate"
            Tags { "LightMode"="UniversalForward" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4  _BaseColor;
                half   _Metallic;
                half   _Smoothness;
            CBUFFER_END
            TEXTURE2D(_BaseMap);          SAMPLER(sampler_BaseMap);
            TEXTURE2D(_MetallicGlossMap); SAMPLER(sampler_MetallicGlossMap);

            // 阈值用 sRGB 0~1 表示(美术在取色器里看到的数值 / 255)。
            // 全局变量,由 C# 用 Shader.SetGlobalVector 设置;w <= 0 时使用下面的默认值。
            float4 _PBRValidateRange;   // x 电介质最暗  y 电介质最亮  z 金属最暗
            #define DEFAULT_RANGE float3(30.0 / 255.0, 240.0 / 255.0, 180.0 / 255.0)
            #define METAL_GRAY_LOW  0.1
            #define METAL_GRAY_HIGH 0.9

            struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; };
            struct Varyings   { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 normalWS : TEXCOORD1; };

            Varyings vert(Attributes v)
            {
                Varyings o;
                o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
                o.normalWS = TransformObjectToWorldNormal(v.normalOS);
                o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                half3 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv).rgb * _BaseColor.rgb;   // 线性
            #if defined(_METALLICSPECGLOSSMAP)
                half metallic = SAMPLE_TEXTURE2D(_MetallicGlossMap, sampler_MetallicGlossMap, i.uv).r;
            #else
                half metallic = _Metallic;
            #endif

                float3 range = _PBRValidateRange.w > 0 ? _PBRValidateRange.xyz : DEFAULT_RANGE;
                // 回到 sRGB 再比较,和美术在 Photoshop/Substance 里看到的数值一致
                half lumS = LinearToSRGB(Luminance(albedo).xxx).x;
                half maxS = LinearToSRGB(max(albedo.r, max(albedo.g, albedo.b)).xxx).x;

                half3 flag = 0; half bad = 0;
                if (metallic > METAL_GRAY_LOW && metallic < METAL_GRAY_HIGH) { flag = half3(1, 0.85, 0); bad = 1; }   // 黄
                else if (metallic >= METAL_GRAY_HIGH)
                {
                    if (maxS < range.z) { flag = half3(1, 0, 1); bad = 1; }                                          // 品红
                }
                else
                {
                    if (lumS < range.x)      { flag = half3(1, 0.1, 0.1); bad = 1; }                                // 红
                    else if (lumS > range.y) { flag = half3(0.15, 0.35, 1); bad = 1; }                              // 蓝
                }

                // 底图:Albedo 亮度 × 简单的半兰伯特,保留形体,又不让光照干扰“材质值”的判断
                half ndl = dot(normalize(i.normalWS), GetMainLight().direction) * 0.5 + 0.5;
                half3 baseCol = Luminance(albedo).xxx * (0.35 + 0.65 * ndl);

                // 屏幕空间斜条纹:一眼能和“本来就是这个颜色的材质”区分开
                half stripe = frac((i.positionCS.x + i.positionCS.y) / 14.0) < 0.5;
                half3 c = lerp(baseCol, flag, bad * lerp(0.35, 0.9, stripe));
                return half4(c, 1);
            }
            ENDHLSL
        }
    }
}