UnityのShader勉強7 ステンシルマスク
ステンシルについておさらいします。
参考:
ステンシルとはピクセルごとに数値を保存しておき、それを比較し描画するかどうかを判断することができます。
これには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側を手前とし重なるように配置します。
わかりやすいように色も変えておきます。
さてまずは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はバッファには書き込まず、保持します。(省略可)
結果
書き込まれた部分だけ描画されました。
ステンシルマスクをしたいときは、これだけ覚えておけば充分実用的です。
比較には、alwaysやequalだけでなく色々あります。
Passはパスしたときの操作ですが、Failでパスしなかった場合の操作もできます。
あとレンダリング順も重要で、今回はどちらもGeometryで描画しましたが、
WriteStencilシェーダーよりも先にReadStencilシェーダーを描画してしまうと、当然うまく比較できません。(ステンシルバッファlの初期値は0)
あと重要なのは深度テストです。
同じレンダリング順でも描画はカメラに近いほうからおこなわれるため、Writeオブジェクトの後ろにReadオブジェクトをおくと描画されません。
詳しくは
に書いてありますが、ステンシルテストにパスしても深度テストをパスしてないと描画できません。
ZTest Always と書くことで深度テストを無効にできるので奥行きを気にしなくてもよくなります。
最後に
簡単ですがステンシルテストをおさらいしてみました。
このあたりから、だいぶレンダリング順などに苦手意識がなくなってきました。
次回はGrabPassをおさらいします。