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

ShaderLab: ステンシル

ステンシルバッファは、一般的にピクセルマスクごとにピクセルの保存や廃棄を行うために使用されます。

ステンシルバッファは、通常、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 レンダリングパス

ベースパスとライティングパスの間でステンシルバッファが他の目的のために使われるように、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
    } 
}

結果:

ShaderLab: Pass 内のタグ
ShaderLab: Name