以下の内容はhttps://www.karvan1230.com/entry/2021/06/08/220121より取得しました。


【Unity】ステンシルバッファを使って窓を作る

f:id:Karvan:20210608213544j:plain

全く話題にならない神ゲーム

後学の為に色んな実況者のゲーム実況を観ていたらYouTubeのお勧めが何故かVtuberの切り抜きで溢れている皆さんこんにちは。ロックマン2の実況が面白くて色々観てたらこうなった。でも、Vtuber同じような名前大杉。

そんな中、久しぶりに購入したゲームが神ゲーでした。タイトルは『The Pathless(ザ・パスレス)』。最近流行ってきたトゥーン調のオープンワールドゲームで、『ABZU』の開発会社による新作アクションパズルゲームです。

f:id:Karvan:20210608213647j:plain

美しいグラフィックと爽快感のある操作性が最大の魅力で、フィールド内を移動するだけでも楽しい。加えてフィールド内に点在するパズルもプレイヤーの観察力を問われる内容で程よい難易度。本当に神ゲーと呼ぶにふさわしいと思うのですが、巷では全く話題になってません。実況動画も攻略サイトも殆どないし。

思うにオープンワールドながらもゲームのメインがパズルなのでボス戦以外に戦闘とかないし、NPCと会話や取引するみたいなイベントもなく全く実況向きでない、実際にプレイしてみないと良さが殆ど伝わらない事が原因なのではないかと。

個人的にあまりに惜しいのでこのブログで紹介しています。
とにかく爽快感のあるゲームがやりたい方、異世界を自由に探索するゲームに興味がある方にはうってつけのタイトルだと思いますので、是非、プレイしてください。

 

 ステンシルバッファ

unityのシェーダでステンシルバッファは描画の際に当該のピクセルを画面に表示するかしないかを決める「マスク」を設定する役目を持ちます。

要はマスクなので「描画したい部分」「描画したくない部分」を明確に定義できるというわけです。

ステンシルバッファの操作はStencilのプロパティを使用します。Stencilのプロパティの宣言は次のようになります。

Ref ステンシルに書き込む値
Comp 使用する比較関数
Pass 比較関数が真のときにの操作

 

 ・比較関数

Greater ステンシル値がバッファ値より大きいピクセルのみレンダリング
GEqual ステンシル値がバッファ値以上のピクセルのみレンダリング
Less ステンシル値がバッファ値より小さいピクセルのみレンダリング
LEqual ステンシル値がバッファ値以下のピクセルのみレンダリング
Equal ステンシル値がバッファ値と等しいピクセルのみレンダリング
NotEqual ステンシル値がバッファ値と等しくないピクセルのみレンダリング
Always 常にステンシル テストは成功
Never 常にステンシル テストは失敗

 

・処理

Keep バッファの現在コンテンツを保持します
Zero バッファにゼロを書き込みます
Replace ステンシル値をバッファに書き込みます
IncrSat バッファの現在値を増分させる
DecrSat バッファの現在値を減分させる
Invert すべてのビットを無効にする
IncrWrap バッファの現在値を増分する。値がすでに 255 の場合は 0 にする
DecrWrap バッファの現在値を減分する。値がすでに 0 の場合は 255 にする

 

窓を作る

「描画したくない部分」を作れると言う事は3Dモデルの形状を変更せずに穴を開けられる、と言う事でそれを利用して窓を作ってみます。手順は

  1. 窓となるオブジェクトにStencilでマークするシェーダを適用する
  2. 壁側のオブジェクトにはStencilでマークされた場所以外を描画するシェーダを適用

となります。

まず最初に窓側のオブジェクト用に透明shaderを作ります。

Shader "WriteStencil"
{
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            
           #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // 透明にする
                return fixed4(0, 0, 0, 0);
            }
            ENDCG
        }
    }
}

 このシェーダにStencilのプロパティを追加します。(CGPROGRAMの上あたり)

// ステンシルバッファの設定
Stencil
{
    // ステンシルの番号
    Ref 2
    
    // Always: このシェーダでレンダリングされたピクセルのステンシルバッファを「対象」とするという意味
    Comp Always
    
    // Replace: 「対象」としたステンシルバッファにRefの値を書き込む、という意味
    Pass Replace
}

 

壁を作る

壁用に標準的なStandardシェーダを作ります。これはCreateメニューから選んで作ることができます。

f:id:Karvan:20210608215255p:plain

 

この作成したStandardシェーダにStencilのプロパティを追加します。

// 壁側のStencil
Stencil
{
    // ステンシルの番号
    Ref 2
    
    // NotEqual: ステンシル番号がバッファ値と等しくないステンシル番号のピクセルのみレンダリング
    Comp NotEqual
}

 

 結果

作った窓シェーダーと壁シェーダーをそれぞれ3dモデルに適用してみます。

f:id:Karvan:20210608215756g:plain

壁を透過して背景が表示されているのが分かると思います。
ステンシルバッファを利用してモデルを変更することなく窓を作ることができました。




以上の内容はhttps://www.karvan1230.com/entry/2021/06/08/220121より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14