LoginSignup
58
44

More than 5 years have passed since last update.

【Unity】ステンシルバッファの復習

Last updated at Posted at 2017-06-09

はじめに

ステンシルバッファへの理解を深めるため、ステンシルバッファによってオブジェクトがどのようにして描画されていくのかを整理してみました。

環境

Windows 10
Unity 5.6.1f1

リファレンス値とバッファ値

Unityのステンシルテストではリファレンス値とバッファ値を使って3Dオブジェクトを描画させるか否かを決定しているそうです。

画面の各ピクセルはバッファ値を持っており、これから描画させようとしている3Dオブジェクトはリファレンス値を持っている。
この二つを比較することで描画させるか否かを判定しているそうです。

参考
https://docs.unity3d.com/jp/540/Manual/SL-Stencil.html

ステンシルバッファの挙動を見てみる

ステンシルバッファの挙動を見るため、シーンに赤色と緑色の板を以下のように置くことを考えます。

それぞれの板にはステンシルが定義されたシェーダーがアタッチされています。

赤の板を手前に出す

シェーダーのステンシルを以下のように設定すると、緑の板の手前に赤の板が表示されるようになります。

赤い板のシェーダーのステンシル
Stencil {
    Ref 2 // リファレンス値
    Comp Always  // 常にステンシルテストをパスさせます。
    Pass Replace    // リファレンス値をバッファに書き込みます。
}
緑の板のシェーダーのステンシル
Stencil {
    Ref 1 // リファレンス値
    Comp Greater // ピクセルのリファレンス値がバッファの値より大きい場合のみレンダリングします。
}

なぜこうなるのか整理してみる

なぜこのような描画結果になるのか私には理解できませんでした。
そこで、どのようにして板が描画されていくのかを整理してみることにしました。

STEP 1. 赤の板の描画

赤の板のステンシルでは Ref 2 が指定されています。
これはリファレンスの値を2にする、という意味の命令らしいです。
そしてPass Replaceによってリファレンスの値が画面のバッファへと書きこまれます。
このときの画面の状態は以下のようになります。

STEP 2. 緑の板の描画

緑の板のステンシルには Ref 1 が指定されています。
つまり、緑の板の描画時のリファレンスの値が1になります。



ここで、緑の板のステンシルにはComp Greaterと記述されています。
これは
ピクセルのリファレンス値がバッファの値より大きい場合のみレンダリングする
という意味の命令らしいです。

これを可視化すると以下のようになります。



結果として赤い板が描画されていない部分で緑色の板が描画されることになります。

最初から何も描画されていない領域のバッファーの値は0になっているみたいです。

最終的な描画結果は以下のようになります。

緑の板をくり抜く

緑の板のシェーダーのステンシルを以下のように設定すると緑の板が赤の板でくり抜かれます。

  Stencil {
      Ref 2
      Comp Equal // ピクセルのリファレンス値がバッファの値と等しい場合のみレンダリングします。
  }

なぜこうなるのか整理してみる

緑の板のステンシルではComp Equal が指定されているため、緑の板が描画されるのはバッファ値 = 2 となっている領域に限定されます。
赤い板が描画されている領域はバッファ値 = 2 ですが、それ以外の領域のバッファ値は2ではありません。

つまり、赤い板の表示領域だけで緑の板が描画されるので以下のような描画結果になります。

緑の板のリファレンス値を変える

緑の板のシェーダーのステンシルのリファレンス値を0にしてやると、赤の板以外の領域で緑の板が描画されるようになります。

  Stencil {
      Ref 0
      Comp Equal // ピクセルのリファレンス値がバッファの値と等しい場合のみレンダリングします。
  }



これは、何も描画されていない領域のバッファ値が0になっているのが理由だと考えられます。

参考URL

Unity - ShaderLab: ステンシル
https://docs.unity3d.com/jp/540/Manual/SL-Stencil.html

[Unity] UnityのShaderでステンシルバッファを試す
http://qiita.com/edo_m18/items/95a7f350d1164486e03b

シェーダーコード

実際に使用したシェーダーです。

赤の板のシェーダー
Shader "Red" {
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}

        Stencil {
            Ref 2 // リファレンス値
            Comp Always  // 常にステンシルテストをパスさせます。
            Pass Replace    // リファレンス値をバッファに書き込みます。
        }

        Pass {

            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
        }
    } 
}
緑の板のシェーダー
Shader "Green" {
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}

        Stencil {
            Ref 0  // リファレンス値
            Comp Equal // ピクセルのリファレンス値がバッファの値と等しい場合のみレンダリングします。
        }

        Pass {
            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
        }
    } 
}
58
44
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
58
44