ステンシルバッファはピクセルマスクごとにピクセルを保存や廃棄することを目的として使用できます。
ステンシルバッファは、通常、1 ピクセルあたり 8 ビットの整数です。値はインクリメントまたはデクリメント書き込みできます。後続のドロー呼び出しは、ピクセルシェーダーを実行する前にピクセルを廃棄する必要があるか判断するために値をテストすることができます。
Ref referenceValue
比較した結果、Comp が always より何かがある場合、値がバッファに 0 〜 255 の整数で書き込まれます( Pass、Fail または ZFail のどれかを置き換えるために設定している場合)。
ReadMask readMask
0–255 の整数として 8 ビットマスクはバッファの内容と基準値を比較する場合に使用されます( referenceValue & readMask ) comparisonFunction ( stencilBufferValue & readMask )。デフォルト: 255 .
WriteMask writeMask
0–255 の整数の 8 ビットマスク。バッファーに書き込むときに使用します。他の書き込みマスク同様、書き込みによってステンシルバッファーのどのビットが影響されるかを指定します (つまり、WriteMask 0 は影響されるビットはなく、0 は書き込まれません)。デフォルト: 255
Comp comparisonFunction
関数はバッファの現在の内容と基準値の比較に使用されます。デフォルト: always 。
Pass stencilOperation
ステンシルテスト(及びデプステスト)をパスした場合、バッファの内容をどうするか決めます。デフォルト: keep 。
Fail stencilOperation
ステンシルテストが失敗した場合、バッファの内容をどうするか決めます。デフォルト: keep 。
ZFail stencilOperation
ステンシルテストにパスし、デプステストを失敗した場合、バッファの内容をどうするか決めます。デフォルト: keep 。
Comp、Pass、Fail、ZFail は正面に面したジオメトリに適用され、Cull Front が指定されていない限り、その場合、それは背面に面したジオメトリです。また、明示的に、(正面に面したジオメトリ) CompFront、PassFront、FailFront、ZFailFront と(背面に面したジオメトリー) CompBack、PassBack、FailBack、ZFailBack を定義することにより、両面ステンシルの状態を指定することができます。
比較関数は、以下のいずれかを使用します。
Greater | ピクセルのレファレンス値がバッファの値より大きい場合のみレンダリングします。 |
GEqual | ピクセルのレファレンス値がバッファの値より大きいか等しい場合のみレンダリングします。 |
Less | ピクセルのレファレンス値がバッファの値より小さい場合のみレンダリングします。 |
LEqual | ピクセルのレファレンス値がバッファの値より小さいか等しい場合のみレンダリングします。 |
Equal | ピクセルのレファレンス値がバッファの値と等しい場合のみレンダリングします。 |
NotEqual | ピクセルのレファレンス値がバッファの値と等しくない場合のみレンダリングします。 |
Always | 常にステンシルテストをパスさせます。 |
Never | 常にステンシルテストを通しません。 |
ステンシル操作は次のいずれかになります。
Keep | バッファの現在コンテンツを保持します。 |
Zero | バッファに 0 を書き込みます。 |
Replace | リファレンス値をバッファに書き込みます。 |
IncrSat | バッファの現在値をインクリメントさせます。値がすでに 255 の場合は 255 のままです。 |
DecrSat | バッファの現在値をデクリメントさせます。値がすでに 0 の場合は 0 のままです。 |
Invert | すべてのビットを反転させます。 |
IncrWrap | バッファの現在値をインクリメントさせます。値がすでに 255 の場合は 0 にします。 |
DecrWrap | バッファの現在値をデクリメントさせます。値がすでに 0 の場合は 255 にします。 |
ベースパスとライティングパスの間でステンシルバッファが他の目的のために使われるように、deferred レンダリングパスでレンダリングするオブジェクトのためのステンシル機能はいくらか制限されます。それらの 2 つのステージの間、シェーダーで定義されるステンシルステートは無視され、最終的なパスの間に考慮されるだけです。そのため、ステンシルテストに基づいてこれらのオブジェクトをマスクすることはできませんが、まだバッファの内容を変更することができます。そして、フレームの後のレンダリングオブジェクトによって使われます。deferred パス以下のフォワードレンダリングパスでのオブジェクトレンダリング(例えばサーフェスシェーダーのない透明なオブジェクトやオブジェクト)は、ステンシルステートを再度設定します。
deferred レンダリングパスは、ステンシルバッファの上位 3 ビットを使用しています。シーンで使用されているライトマスクレイヤーの数次第で、4 ビットまでさらに上位に加えられます。これは、読み取りおよび書き込みをステンシルマスクを使用して、“clean”なビットの範囲内で操作することが可能であり、または、Camera.clearStencilAfterLightingPass を使用しているライティングパスの後、ステンシルバッファをきれいにカメラを強制することができます。
最初の例は、深度テストがパスすると、値 2 を書き込みます。ステンシルテストは常にパスするように設定されています。
Shader "Red" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Stencil {
Ref 2
Comp always
Pass replace
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(1,0,0,1);
}
ENDCG
}
}
}
第二のシェーダーは、最初の(赤)シェーダーをパスしたピクセルのために渡します。それは値 ‘2’ と等しいかどうかをチェックするためです。それはまた、ステンシルテストを失敗したところでバッファーの値をデクリメントします。
Shader "Green" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
Pass {
Stencil {
Ref 2
Comp equal
Pass keep
ZFail decrWrap
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(0,1,0,1);
}
ENDCG
}
}
}
第 3 のシェーダーはステンシル値が ‘1’ のときにだけパスします。それで、赤と緑の球が交差した点のピクセルだけ、つまり、ステンシルが赤のシェーダーにより ‘2’ に設定され、かつ、緑シェーダーにより ‘1’ ずつデクリメントされます。
Shader "Blue" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry+2"}
Pass {
Stencil {
Ref 1
Comp equal
}
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(0,0,1,1);
}
ENDCG
}
}
}
結果:
別のより直接的な効果の例 . 球は、ステンシルバッファーでこのシェーダーをマークアップするための適切な領域を最初にレンダリングされます。
Shader "HolePrepare" {
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
ColorMask 0
ZWrite off
Stencil {
Ref 1
Comp always
Pass replace
}
CGINCLUDE
struct appdata {
float4 vertex : POSITION;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f i) : SV_Target {
return half4(1,1,0,1);
}
ENDCG
Pass {
Cull Front
ZTest Less
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass {
Cull Back
ZTest Greater
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
そして、より標準的なサーフェースシェーダーとしてもう一度レンダリングされます。選別した前面を除いて、抑制されたデプステストとステンシルテストは、前にマークされたピクセルを捨てるよう試みます。
Shader "Hole" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0)
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry+2"}
ColorMask RGB
Cull Front
ZTest Always
Stencil {
Ref 1
Comp notequal
}
CGPROGRAM
#pragma surface surf Lambert
float4 _Color;
struct Input {
float4 color : COLOR;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = _Color.rgb;
o.Normal = half3(0,0,-1);
o.Alpha = 1;
}
ENDCG
}
}
結果: