[Unity] Stencil Buffer 導入～

# ステンシルバッファ，ステンシルテスト
ステンシルバッファとはすべてのピクセルが保持している非負の整数値(8bit)のことです．描画するモデル(material１つ単位)でステンシルバッファの値が決められていてその値と，これから書き込むピクセルのステンシルバッファに保持されている値を比較してそのモデルをフラグメント単位で描画するかしないかを決定することが出来ます．

# ステンシル値を書き込んでみる
まず１番シンプルな形から書いていきます．

```stencil.hlsl
Shader "Hidden/Stemcil01"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Cull Off ZWrite Off ZTest Always

        Pass
        {
　　　　　　Stencil{
		　　　　Ref 1 
		　　　　Comp Less
		　　　　Pass Replace    
	　　　　}


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
				return half4(0,0.8,0.9,1);
            }
            ENDCG
        }
    }
}

```

vertexやfragmentのコードも一緒に乗せてはいるんですが大切なのはほんの一部です．今後のサンプル掲載は長くなってしまうのでこの部分だけ載せますね．

```stencil-snipet.hlsl
Stencil{
	Ref 1 
	Comp Greater//比較処理方法の指定
	Pass Replace//比較処理に合格した際の処理内容
}

```
###結果
これで青いPlaneを描画する際に参照値(今回は1)がこれから描画したい領域のステンシルバッファ値よりも大きいとき(Greater)その領域のステンシルバッファの値を1に書き換える(Replace)っていうサンプルです．

![bandicam 2019-09-16 18-14-04-618.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184790/66beb589-f0ea-64cf-994f-e3d8613549db.jpeg)

# デフォルトのステンシルバッファ値
次にデフォルトでのステンシルバッファの値が気になってきます．予想もつくかもしれませんが0です．念のため確認していきましょう．

```stencil-snipet.hlsl
Stencil{
	Ref 0
	Comp Equal
	Pass Replace    
}
```
先ほどのように考えていきましょう．これから描画したい領域のステンシルバッファの値が0(Ref値)と等しい(Comp Equal)ならば描画するという物です．よってデフォルトのバッファが0であることがわかりました．
###結果
![bandicam 2019-09-16 18-14-04-618.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184790/66beb589-f0ea-64cf-994f-e3d8613549db.jpeg)


## 深度情報による位置関係を崩してみる
こんな感じでデプス関係では明らかに青いplaneの方が前に来ているにも関わらずピンクのplaneが前方に現れるものを作ってみます．
![bandicam 2019-09-16 18-26-13-509.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184790/654532c8-fb9c-7e8c-0765-016ce4e88332.jpeg)


```stencil-pink.hlsl
Stencil {
	Ref 1 
	Comp Always
	Pass Replace
}

```

```stencil-blue.hlsl
Stencil {
	Ref 2 
	Comp Greater
	Pass Replace
}

```

こんな感じでblueの方はこれから書き込みたい領域のバッファ値と2を比べて大きければ描画します．しかしpinkの方でのステンシルテストの方法alwaysにすると常にステンシルテストをpassすることになってしまうためこのようなことがおきます．


## マルチパス

![ezgif.com-optimize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184790/3996ac12-db36-b1b0-a87f-1b17a81fd2c9.gif)


青いplaneは取り合えず2でバッファ値を埋めます．

```stencil-blue.hlsl
Stencil{
			Ref 2
			Comp Greater
			Pass Replace    
		}
```

ピンクのplaneは背景(何も書かれていないところ)つまりバッファの値が0の部分の領域に描く際にはピンクを1pass目で指定して，先ほど指定した青いplaneによって書き込まれたバッファ値2と等しい際にはグレーを指定しています．


```
Shader "Hidden/Stemcil01"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {

        Pass
        {

			Stencil {
				Ref 0
				Comp Equal
				Pass Replace
			}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(0.9, 0.0, 0.85, 1.0);
            }
            ENDCG
        }

		Pass
		{
			Stencil {
				Ref 2
				Comp Equal
				Pass Replace
			}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

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

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

			v2f vert(appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			sampler2D _MainTex;

			fixed4 frag(v2f i) : SV_Target
			{
				return fixed4(0.2, 0.2, 0.2, 1.0);
			}
			ENDCG
		}
    }
}

```

# まとめ
導入にすぎませんが色々と面白い活用事例があるので調べてみるとより学ぶモチベーションが上がるかもしれませんね．活用事例がまとまったサイトを１つ上げておきます．
http://tsubakit1.hateblo.jp/entry/2015/11/22/065743

## サンプル
vertexもいじってみました．区間内で球体が何か変化が起きているような印象？．
![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184790/19a2ffbc-b011-92e0-28db-a8b288cfd6bc.gif)