You can use the stencil buffer as a general purpose per pixel mask for saving or discarding pixels.
ステンシルバッファは、通常、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 になります。 |
Stencil functionality for objects rendered in the deferred rendering path is somewhat limited, as during the G-buffer pass and lighting pass, Unity uses the stencil buffer for other purposes. During those two stages, the stencil state defined in the shader will be ignored. Because of that, you cannot mask out these objects based on a stencil test, but they can still modify the buffer contents, to be used by objects rendered later in the frame. Objects rendered in the forward rendering path following the deferred path (e.g. transparent objects or objects without a surface shader) will set their stencil state normally again.
These bits are used for the stencil buffer in the deferred rendering path:
It is possible to operate within the range of the unused bits using the stencil read and write masks, or you can force the camera to clean the stencil buffer after the lighting pass using 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
}
}
結果: