Version: 2019.4
ShaderLab: Pass 内のタグ
ShaderLab: Name

ShaderLab: ステンシル

You can use the stencil buffer as a general purpose per pixel mask for saving or discarding pixels.

ステンシルバッファは、通常、1 ピクセルあたり 8 ビットの整数です。値はインクリメントまたはデクリメントで書き込みできます。後続の描画呼び出しは値をテストでき、ピクセルシェーダーを実行する前にピクセルを廃棄する必要があるか判断します。

シンタックス

Ref

    Ref referenceValue

比較した結果、Comp が always でない場合、値がバッファに 0 〜 255 の整数で書き込まれます (Pass、Fail、ZFail のいずれかを置き換える設定をしている場合)。

ReadMask

    ReadMask readMask

0–255 の整数の 8 ビットマスク。基準値 ( referenceValue & readMask ) とバッファの内容 comparisonFunction ( stencilBufferValue & readMask ) とを比較する場合に使用されます。デフォルト: 255

WriteMask

    WriteMask writeMask

0–255 の整数の 8 ビットマスク。バッファーに書き込むときに使用します。他の書き込みマスク同様、書き込みによってステンシルバッファーのどのビットが影響されるかを指定します (つまり、WriteMask 0 は影響されるビットはなく、0 は書き込まれません)。デフォルト: 255

Comp

    Comp comparisonFunction

関数はバッファの現在の内容と基準値の比較に使用されます。デフォルト: always

Pass

    Pass stencilOperation

ステンシルテスト(及びデプステスト)をパスした場合、バッファの内容をどうするか決めます。デフォルト: keep

Fail

    Fail stencilOperation

ステンシルテストが失敗した場合、バッファの内容をどうするか決めます。デフォルト: keep

ZFail

    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 レンダリングパス

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:

  • Bit #7 (value=128) indicates any non-background object.
  • Bit #6 (value=64) indicates non-lightmapped objects.
  • Bit #5 (value=32) is not used by Unity.
  • Bit #4 (value=16) is used for light shape culling during the lighting pass, so that the lighting shader is only executed on pixels that the light touches, and not on pixels where the surface geometry is actually behind the light volume.
  • Lowest four bits (values 1,2,4,8) are used for light layer culling masks.

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
    } 
}

結果:

ShaderLab: Pass 内のタグ
ShaderLab: Name