GameTech/URP 渲染实战
源码 / Ch24/SGKeywordEquivalent.shader
Ch24/SGKeywordEquivalent.shader
// 第 24 章:Blackboard Keyword 生成的代码长什么样。
// Graph 里建了两个 Keyword:
//   Enum    “Pattern”,Reference = _PATTERN,Entries = STRIPES / DOTS / CHECKER,Definition = Shader Feature,Scope = Local
//   Boolean “Use Rim”,Reference = _USE_RIM_ON,Definition = Shader Feature,Scope = Local
// Shader Graph 会为它们生成下面 Properties 里的两行和 Pass 里的两行 #pragma —— 与手写完全一致。
Shader "Cookbook/Ch24/SGKeywordEquivalent"
{
    Properties
    {
        [KeywordEnum(Stripes, Dots, Checker)] _PATTERN ("Pattern", Float) = 0
        [Toggle] _USE_RIM ("Use Rim", Float) = 0
        _ColorA ("Color A", Color) = (0.95, 0.85, 0.6, 1)
        _ColorB ("Color B", Color) = (0.25, 0.45, 0.8, 1)
        _Tiling ("Tiling", Float) = 8
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }
        Pass
        {
            Name "Universal Forward"
            Tags { "LightMode"="UniversalForward" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature_local _PATTERN_STRIPES _PATTERN_DOTS _PATTERN_CHECKER
            #pragma shader_feature_local _ _USE_RIM_ON
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            CBUFFER_START(UnityPerMaterial)
                half4 _ColorA;
                half4 _ColorB;
                float _Tiling;
            CBUFFER_END

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

            Varyings vert(Attributes v)
            {
                Varyings o;
                VertexPositionInputs p = GetVertexPositionInputs(v.positionOS.xyz);
                o.positionCS = p.positionCS; o.positionWS = p.positionWS;
                o.normalWS = TransformObjectToWorldNormal(v.normalOS);
                o.uv = v.uv;
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                float2 uv = i.uv * _Tiling;
                half mask;
                // 一个 Enum Keyword 节点 = 一组 #if/#elif,每个分支对应节点上的一个输入口
            #if defined(_PATTERN_STRIPES)
                mask = step(0.5, frac(uv.x + uv.y));
            #elif defined(_PATTERN_DOTS)
                mask = step(length(frac(uv) - 0.5), 0.3);
            #else // _PATTERN_CHECKER
                float2 c = floor(uv);
                mask = fmod(c.x + c.y, 2.0);
            #endif
                half3 albedo = lerp(_ColorA.rgb, _ColorB.rgb, mask);

                half3 n = normalize(i.normalWS);
                Light l = GetMainLight();
                half3 color = albedo * (l.color * saturate(dot(n, l.direction)) + SampleSH(n));

                // Boolean Keyword 节点 = 一个 #if,On / Off 两个输入口
            #if defined(_USE_RIM_ON)
                half3 v = GetWorldSpaceNormalizeViewDir(i.positionWS);
                color += pow(1 - saturate(dot(n, v)), 3) * half3(1.0, 0.9, 0.7) * 1.5;
            #endif
                return half4(color, 1);
            }
            ENDHLSL
        }
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex Ch23DepthOnlyVert
            #pragma fragment Ch23NullFrag
            #include "Assets/Cookbook/Ch23/Ch23Common.hlsl"
            ENDHLSL
        }
    }
}