Version: 5.6
ShaderLab : Pass 内の Tags
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’ と等しいかどうかをチェックするためです。それはまた、ステンシルテストを失敗したところでバッファーの値をデクリメントします。

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 内の Tags
ShaderLab: Name