読者です 読者をやめる 読者になる 読者になる

しゅみぷろ

プログラミングとか

シンプルなMatcapシェーダーを書いてみる

Shader Shading Unity

はじめに

UnityでMatcapシェーダー書いたのでメモ。

Matcapシェーダーは考え方とか実装がめちゃくちゃ簡単なので、シェーダーの勉強に良さそうです。

Matcapの特徴はとにかく軽量で良い感じに写実的な表現が行える上、実装が簡単なことだと思います。

Matcap

レンダリングされた球の画像を使うことでシェーディングを行う手法のことだと思います(多分)。

考え方はとてもシンプルで、レンダリングするオブジェクトの法線を球の画像に射影するだけです。

球の画像はこんな感じで与えます。

f:id:es_program:20170128223022p:plain:w500

適当に作ったMatcap用の画像ですが、これ使ってレンダリングするとこんな感じに。

f:id:es_program:20170128223400p:plain:w500

モデルにはこちらのアセットを使わせて頂きました。

Asset Storeでシェーダーと画像を入手できるので自分で書く必要はないのですが、自分で書けるとそれなりにカスタマイズできて面白いです。

シンプルなMatcapシェーダー

とりあえずコードはこんな感じです。

Shader "Custom/SimpleMatcap"
{
    Properties
    {
        _Matcap ("Matcap Texture", 2D) = "white" {}
        _Range ("Range", Range(0, 1.5)) = 0
    }
    SubShader
    {
        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 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 normal : TEXCOORD1;//Phong-Shading
            };

            sampler2D _Matcap;
            float _Range;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                o.normal = float4(v.normal, 1);//LocalSpace Normal
                //o.normal = float4(mul((float3x3)_Object2World, v.normal), 1);//WorldSpace Normal
                //o.normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 1));//ProjectionSpace Normal
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 normalProj = normalize(i.normal.xyz) * 0.5 + 0.5;
                fixed4 col = tex2D(_Matcap, normalProj * _Range);
                return col;
            }
            ENDCG
        }
    }
}

Phong-Shadingを行って、ラスタライザに法線を補間して滑らかにしてもらってます。

o.normal = float4(v.normal, 1);//LocalSpace Normal
//o.normal = float4(mul((float3x3)_Object2World, v.normal), 1);//WorldSpace Normal
//o.normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 1));//ProjectionSpace Normal

上の部分では、法線をお好みの座標空間に変換しています。

テカテカした感じの画像を使用する場合、ProjectionSpaceだとちょっといい感じに見えます。

メインのテクスチャを考慮するMatcapシェーダー

左がメインテクスチャのみ、右がMatcap適用後です。

f:id:es_program:20170128230831p:plain:w500

下のような感じで、ブレンディングを調整できます。

f:id:es_program:20170128230845g:plain:w500

テカテカしててなかなかカッコイイです。洞窟にいそう。

コードはこんな感じ。

Shader "Unlit/BlendMatcap"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _Matcap ("Matcap Texture", 2D) = "white" {}
        _Range ("Range", Range(0, 1.5)) = 1
        _Blend ("Blend", Range(0, 1)) = 0.5
    }
    SubShader
    {
        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 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 normal : TEXCOORD1;//Phong-Shading
            };

            sampler2D _MainTex;
            sampler2D _Matcap;
            float _Range;
            float _Blend;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                //o.normal = float4(v.normal, 1);//LocalSpace Normal
                //o.normal = float4(mul((float3x3)_Object2World, v.normal), 1);//WorldSpace Normal
                o.normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 1));//ProjectionSpace Normal
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 normalProj = normalize(i.normal.xyz) * 0.5 + 0.5;
                float4 matcap = tex2D(_Matcap, normalProj * _Range);
                float4 main = tex2D(_MainTex, i.uv);
                float4 col = lerp(main, matcap, _Blend);
                return col;
            }
            ENDCG
        }
    }
}

さいごに

まだまだ色々カスタマイズしてみたいと思います。