GameTech/URP 渲染实战
源码 / Ch08/Matcap.shader
Ch08/Matcap.shader
// Matcap(材质捕获):把“一颗已经打好光的球”的照片,按观察空间法线贴到物体上。
// 作为环境光照的对照:它便宜、好调,但光照永远跟着相机走,与场景无关。
Shader "Cookbook/Ch08/Matcap"
{
    Properties
    {
        [NoScaleOffset] _MatCap("MatCap", 2D) = "gray" {}
        _Tint("Tint", Color) = (1, 1, 1, 1)
        [Toggle] _PerspectiveFix("Perspective Fix", Float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        CBUFFER_START(UnityPerMaterial)
            half4 _Tint;
            half _PerspectiveFix;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Name "Unlit"
            Tags { "LightMode"="UniversalForwardOnly" }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            TEXTURE2D(_MatCap); SAMPLER(sampler_MatCap);

            struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; };
            struct Varyings { float4 positionCS : SV_POSITION; float3 normalVS : TEXCOORD0; float3 positionVS : TEXCOORD1; };

            Varyings vert(Attributes input)
            {
                Varyings o;
                float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                o.positionCS = TransformWorldToHClip(positionWS);
                o.positionVS = TransformWorldToView(positionWS);
                o.normalVS = TransformWorldToViewDir(TransformObjectToWorldNormal(input.normalOS));
                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                float3 n = normalize(i.normalVS);
                float2 uv;
                if (_PerspectiveFix > 0.5)
                {
                    // 透视修正:用“视线方向”构造一个局部坐标系,再把法线投影进去。
                    // 否则物体在画面边缘时,Matcap 会被明显拉扯(只有正交相机下 n.xy 才是准确的)
                    float3 v = normalize(i.positionVS);          // 观察空间里从相机指向表面
                    float3 r = cross(v, n);
                    uv = float2(-r.y, r.x);
                }
                else
                {
                    uv = n.xy;                                   // 经典写法:直接取观察空间法线的 xy
                }
                uv = uv * 0.5 + 0.5;
                half3 c = SAMPLE_TEXTURE2D(_MatCap, sampler_MatCap, uv).rgb * _Tint.rgb;
                return half4(c, 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            HLSLPROGRAM
            #pragma vertex Ch08DepthVert
            #pragma fragment Ch08NullFrag
            #include "Assets/Cookbook/Ch08/Ch08Passes.hlsl"
            ENDHLSL
        }
    }
}