ステンシルバッファは、一般的にピクセルマスクごとにピクセルの保存や廃棄を行うために使用されます。
ステンシルバッファは、通常、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 のシェーダーは、最初の(赤)シェーダーをパスしたピクセルのために渡します。それは値 ‘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
}
}
結果: