尝试MatCap类型shader

听说MatCap能在低端机上做出很漂亮的pbr效果,就尝试了一下。

MatCap全称MaterailCapture,里面存的是光照信息,通过法线的xy分量去采样matcap,得到在该方向法线的光照信息,因为不是实时光照,所以性能好,好不好看就看你的Matcap做得好不好了。下面是简单的matcap尝试:

Shader "Unlit/SimpleMatCapShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _MatCap("MatCap", 2D) = "white" {}
        _MatCapFactor("MatCapFactor", Range(0,5)) = 1
        _EnvTex("EnvTex (CubeMap)", Cube) = "_SkyBox" {}
        _EnvFactor("EnvStrength", Range(0,1)) = 0.8
    }

    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
            {
                float4 twoUv : TEXCOORD0;//主纹理uv存xy,matCapUv存在zw,这算一种优化
                float4 vertex : SV_POSITION;
                float3 RefDir : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _MatCap;
            half _MatCapFactor;
            samplerCUBE _EnvTex;
            half _EnvFactor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.twoUv.xy = TRANSFORM_TEX(v.uv, _MainTex);
                //matcap存的其实是:法线中xy分量作为uv,对应的光照颜色信息,即用xy去采样就好,注:xy必须是归一化法线中的分量,故z才没必要
                o.twoUv.zw = UnityObjectToWorldNormal(v.normal).xy;
                o.twoUv.zw = o.twoUv.zw * 0.5 + 0.5;//(-1,1)->(0,1)
                float3 wolrdN = UnityObjectToWorldNormal(v.normal);
                o.RefDir = reflect(-WorldSpaceViewDir(v.vertex), wolrdN);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.twoUv.xy);
                fixed4 mapCapCol = tex2D(_MatCap, i.twoUv.zw);
                fixed4 reflection = texCUBE(_EnvTex, i.RefDir);
                col.rgb = col.rgb * mapCapCol.rgb * _MatCapFactor + reflection.rgb * _EnvFactor;
                return col;
            }
            ENDCG
        }
    }
}

各种效果如下:

matCap贴图的获取:

这边提供几个可以获取MatCap贴图的网址:

[1] https://www.pinterest.com/evayali/matcap/

[2]https://www.google.com.hk/search?q=MatCap&newwindow=1&safe=strict&hl=zh-CN&biw=1575&bih=833&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwju8JDTpZnSAhUGn5QKHawODTIQsAQIIg

[3]http://pixologic.com/zbrush/downloadcenter/library/#prettyPhoto

 

总结,如果matcap做得好,确实能得到非常漂亮的效果,关键是,只是多一张tex就能获得如此效果,不错不错。

  许多资料都没有讲到,但我个人以为,matcap其实是缤纷多彩的高光效果。普通高光一般是单一的白色,或其他单色,matcap因为用图片存不同“斜率”的法线的高光颜色,所以可以做得更加漂亮,这个可以看下图:

                            

                                一个matcap纹理图

               (因为法线先x,y,z归一化(一个单位球体),后取其中的x,y采用,所以[x,y]必定束缚在圆中,

                        所以matcap都是圆,即使不是,也只有圆内纹理有效)

从上面shader可知我们先把法线从[-1,1]映射到[0,1]后再采样,这个是由于matcap坐标是从0开始的,为了更加直观的理解,我们反其道行之:

  把matcap图片的坐标从[0,1]映射到[-1,1]。

则,明显地,图片中间就是[0,0],即普通笛卡尔坐标系的原点,此时,法线区间是[-1,1],matcap图的坐标区间也是[-1,1],一一对应,每个像素的法线根据其大小和方向在matcap图中取值,得到的就是这个像素的高光颜色,这样,matcap的效果也变得很直观了。

posted @ 2017-05-19 16:07  BigTreee  阅读(7872)  评论(0编辑  收藏  举报