LoginSignup
5
5

More than 3 years have passed since last update.

[Unity] Stencil Buffer 導入~

Last updated at Posted at 2019-09-16

ステンシルバッファ,ステンシルテスト

ステンシルバッファとはすべてのピクセルが保持している非負の整数値(8bit)のことです.描画するモデル(material1つ単位)でステンシルバッファの値が決められていてその値と,これから書き込むピクセルのステンシルバッファに保持されている値を比較してそのモデルをフラグメント単位で描画するかしないかを決定することが出来ます.

ステンシル値を書き込んでみる

まず1番シンプルな形から書いていきます.

stencil.hlsl
Shader "Hidden/Stemcil01"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Cull Off ZWrite Off ZTest Always

        Pass
        {
      Stencil{
            Ref 1 
            Comp Less
            Pass Replace    
        }


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                return half4(0,0.8,0.9,1);
            }
            ENDCG
        }
    }
}

vertexやfragmentのコードも一緒に乗せてはいるんですが大切なのはほんの一部です.今後のサンプル掲載は長くなってしまうのでこの部分だけ載せますね.

stencil-snipet.hlsl
Stencil{
    Ref 1 
    Comp Greater//比較処理方法の指定
    Pass Replace//比較処理に合格した際の処理内容
}

結果

これで青いPlaneを描画する際に参照値(今回は1)がこれから描画したい領域のステンシルバッファ値よりも大きいとき(Greater)その領域のステンシルバッファの値を1に書き換える(Replace)っていうサンプルです.

bandicam 2019-09-16 18-14-04-618.jpg

デフォルトのステンシルバッファ値

次にデフォルトでのステンシルバッファの値が気になってきます.予想もつくかもしれませんが0です.念のため確認していきましょう.

stencil-snipet.hlsl
Stencil{
    Ref 0
    Comp Equal
    Pass Replace    
}

先ほどのように考えていきましょう.これから描画したい領域のステンシルバッファの値が0(Ref値)と等しい(Comp Equal)ならば描画するという物です.よってデフォルトのバッファが0であることがわかりました.

結果

bandicam 2019-09-16 18-14-04-618.jpg

深度情報による位置関係を崩してみる

こんな感じでデプス関係では明らかに青いplaneの方が前に来ているにも関わらずピンクのplaneが前方に現れるものを作ってみます.
bandicam 2019-09-16 18-26-13-509.jpg

stencil-pink.hlsl
Stencil {
    Ref 1 
    Comp Always
    Pass Replace
}

stencil-blue.hlsl
Stencil {
    Ref 2 
    Comp Greater
    Pass Replace
}

こんな感じでblueの方はこれから書き込みたい領域のバッファ値と2を比べて大きければ描画します.しかしpinkの方でのステンシルテストの方法alwaysにすると常にステンシルテストをpassすることになってしまうためこのようなことがおきます.

マルチパス

ezgif.com-optimize.gif

青いplaneは取り合えず2でバッファ値を埋めます.

stencil-blue.hlsl
Stencil{
            Ref 2
            Comp Greater
            Pass Replace    
        }

ピンクのplaneは背景(何も書かれていないところ)つまりバッファの値が0の部分の領域に描く際にはピンクを1pass目で指定して,先ほど指定した青いplaneによって書き込まれたバッファ値2と等しい際にはグレーを指定しています.

Shader "Hidden/Stemcil01"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {

        Pass
        {

            Stencil {
                Ref 0
                Comp Equal
                Pass Replace
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(0.9, 0.0, 0.85, 1.0);
            }
            ENDCG
        }

        Pass
        {
            Stencil {
                Ref 2
                Comp Equal
                Pass Replace
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f i) : SV_Target
            {
                return fixed4(0.2, 0.2, 0.2, 1.0);
            }
            ENDCG
        }
    }
}

まとめ

導入にすぎませんが色々と面白い活用事例があるので調べてみるとより学ぶモチベーションが上がるかもしれませんね.活用事例がまとまったサイトを1つ上げておきます.
http://tsubakit1.hateblo.jp/entry/2015/11/22/065743

サンプル

vertexもいじってみました.区間内で球体が何か変化が起きているような印象?.
ezgif.com-video-to-gif.gif

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5