GameTech/URP 渲染实战
源码 / shaders/Ch08/Matcap.glsl
shaders/Ch08/Matcap.glsl
// Matcap(材质捕获):把“一颗已经打好光的球”的照片,按观察空间法线贴到物体上。(GLSL 版,对应 Ch08/Matcap.shader)
// 作为环境光照的对照:它便宜、好调,但光照永远跟着相机走,与场景无关。
Shader "Cookbook/Ch08/Matcap"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

        GLSLINCLUDE
        uniform vec4  _Tint;
        uniform float _PerspectiveFix;
        ENDGLSL

        Pass
        {
            Name "Unlit"
            Tags { "LightMode"="UniversalForwardOnly" }
            GLSLPROGRAM
            uniform sampler2D _MatCap;

            VARYING vec3 normalVS;
            VARYING vec3 positionVS;

            #ifdef VERTEX
            void main()
            {
                vec3 positionWS = TransformObjectToWorld(positionOS.xyz);
                gl_Position = TransformWorldToHClip(positionWS);
                positionVS = TransformWorldToView(positionWS);
                normalVS = TransformWorldToViewDir(TransformObjectToWorldNormal(normalOS));
            }
            #endif

            #ifdef FRAGMENT
            void main()
            {
                vec3 n = normalize(normalVS);
                vec2 uv;
                if (_PerspectiveFix > 0.5)
                {
                    // 透视修正:用“视线方向”构造一个局部坐标系,再把法线投影进去。
                    // 否则物体在画面边缘时,Matcap 会被明显拉扯(只有正交相机下 n.xy 才是准确的)
                    vec3 v = normalize(positionVS);          // 观察空间里从相机指向表面
                    vec3 r = cross(v, n);
                    uv = vec2(-r.y, r.x);
                }
                else
                {
                    uv = n.xy;                               // 经典写法:直接取观察空间法线的 xy
                }
                uv = uv * 0.5 + 0.5;
                vec3 c = texture(_MatCap, uv).rgb * _Tint.rgb;
                fragColor = vec4(c, 1.0);
            }
            #endif
            ENDGLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R
            GLSLPROGRAM
            #include "Ch08/Ch08Passes.glsl"
            #ifdef VERTEX
            void main() { gl_Position = Ch08DepthVert(); }
            #endif
            #ifdef FRAGMENT
            void main() { fragColor = Ch08NullFrag(); }
            #endif
            ENDGLSL
        }
    }
}