はるのゲーム開発メモ

自分の勉強の成果などをメモ代わりにします

UnityのShader勉強7 ステンシルマスク

ステンシルについておさらいします。

参考:

docs.unity3d.com

ステンシルとはピクセルごとに数値を保存しておき、それを比較し描画するかどうかを判断することができます。

これにはshaderが最低でも二つ必要ですのでまずは簡単に試してみます。

基本となるコードは

Shader "Unlit/StencilTest"
{
	Properties{
		_Texture("Texture",2D)="white"{}
		_Float("float",float) = 1
		_Color("Color",Color) = (1,1,1,1)
		_Range("Range",Range(-10,10))=0.5
		_Vector("ector",Vector)=(0,0,0,0)
	}
	SubShader
	{
		Tags{
			"RenderType"="Transparent"
			"Queue"="Geometry"
		}
		Pass
		{
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			sampler2D _Texture;
			float4 _Texture_ST;

			fixed _Float;
			fixed4 _Color;
			fixed _Range;
			fixed4 _Vector;
			struct appdata
			{
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
			};

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

			v2f vert (appdata v)
			{
				v2f o;
				float4 vec = v.vertex;
				o.vertex = UnityObjectToClipPos(vec);
				o.uv = TRANSFORM_TEX(v.texcoord, _Texture);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				
				fixed4 tex = tex2D(_Texture,i.uv)*_Color;
				return tex;
			}
			ENDCG
		}
	}
}

 

これを改造していきます。

複製し、パスを”Unit/StencilWrite”と”Unit/StencilRead”とします。

マテリアルも二つ作り名前をStencilWriteMat、StencilReadMatとし、shaderを割り当てておきます。

Sphereを二つ作成し作ったマテリアルをアタッチしておきます。

 

配置はWrite側を奥、Read側を手前とし重なるように配置します。

わかりやすいように色も変えておきます。

f:id:Haru_android:20171220202256p:plain

 

さてまずはStencilを書き込みます。

StencilWriteシェーダーを書き加えます。

Pass
{
	Stencil{
		ref 2
		Comp always
		Pass replace
	}
	Blend SrcAlpha OneMinusSrcAlpha
        ~~~~~~~~

ステンシルは、Stencil{}に記述します。

refで比較、書き込みたい数値(0~255)

Compで描画するかどうか比較します。alwaysは常にパスします。

Passはテストにパスした後の操作で、replceで書き込みます。

操作には

Pass ステンシルテストと深度テストをパスした場合
Fail ステンシルテストに失敗した場合
Zfail ステンシルテストにパスし、デプステストに失敗した場合

をが使えます。

 

 

続いて、StencilReadシェーダーも書き加えます。

Pass
{
	Stencil{
		ref 2
		Comp equal
		Pass keep
	}
	Blend SrcAlpha OneMinusSrcAlpha
        ~~~~~~~~

equalは書き込まれている値と同じ場合のみパス

keepはバッファには書き込まず、保持します。(省略可)

 

結果

f:id:Haru_android:20171220203911p:plain

書き込まれた部分だけ描画されました。

ステンシルマスクをしたいときは、これだけ覚えておけば充分実用的です。

 

比較には、alwaysやequalだけでなく色々あります。

Passはパスしたときの操作ですが、Failでパスしなかった場合の操作もできます。

 

あとレンダリング順も重要で、今回はどちらもGeometryで描画しましたが、

WriteStencilシェーダーよりも先にReadStencilシェーダーを描画してしまうと、当然うまく比較できません。(ステンシルバッファlの初期値は0)

 

あと重要なのは深度テストです。

同じレンダリング順でも描画はカメラに近いほうからおこなわれるため、Writeオブジェクトの後ろにReadオブジェクトをおくと描画されません。

詳しくは

www.shibuya24.info

に書いてありますが、ステンシルテストにパスしても深度テストをパスしてないと描画できません。

ZTest Always と書くことで深度テストを無効にできるので奥行きを気にしなくてもよくなります。

 

 

 

最後に

簡単ですがステンシルテストをおさらいしてみました。

このあたりから、だいぶレンダリング順などに苦手意識がなくなってきました。

 

次回はGrabPassをおさらいします。