Matcap Shader

Matcap Shader

Matcap Shader是一种在某些层面能替代甚至超越PBR的次世代渲染方案。

Matcap,全称为Material Capture,翻译过来就是材质捕获。简单来说就是预先生成的一种存储了光照和反射等信息的贴图,运行时使用法线方向进行采样。Matcap仅对于固定相机视角的情况较好,这也是Matcap的原理决定的。

Matcap的原理

物体的法线是三维的,采样的贴图是二维的,所以我们需要去掉一个维度。在相机空间下的物体法线,我们可以不考虑Z轴的指向,即不考虑Z轴本身对屏幕空间的XY平面的贡献。因为法线是一个单位向量,如果法线在XY方向上的投影权重很大,那么说明在Z方向的权重就很小,对应到二维的Matcap上采样的位置越靠近边缘;而如果法线在XY方向的投影权重很小,那么Z方向的权重就很大,对应到二维的Matcap上的采样位置就越靠近中心。

Shader "Custom/MatcapShader"
{
    Properties
    {
        _MatcapTex ("Matcap Tex", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 matcapuv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MatcapTex;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //UNITY_MATRIX_IT_MV 用于 将法线从模型空间转化到视觉空间
                //乘以逆转置矩阵将normal变换到视空间
                float3 viewnormal = mul(UNITY_MATRIX_IT_MV, v.normal);
                //需要normalize一下,否则保证normal处在(-1,1)区间,否则有scale的object效果不对
                viewnormal = normalize(viewnormal);
                o.matcapuv = viewnormal.xy * 0.5 + 0.5;

                //如果缩放模型,这种方式会出现问题,一定要保证ViewNormal处在(-1,1)区间
                //o.matcapuv.x = mul(UNITY_MATRIX_IT_MV[0], v.normal) * 0.5 + 0.5; 
                //o.matcapuv.y = mul(UNITY_MATRIX_IT_MV[1], v.normal) * 0.5 + 0.5; 
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MatcapTex, i.matcapuv);
                return col;
            }
            ENDCG
        }
    }
}