MRが楽しい

MRやVRについて学習したことを書き残す

Hololensで壁の向こうの世界が見える窓を作る その2

hololensの学習枠の続きです。
bluebirdofoz.hatenablog.com

以下の技術ブログを参考にしています。
tips.hecomi.com

今回は「方法1」プロジェクト内のコードを確認して、技術への理解を深めます。

改めてプロジェクトの全体像を確認します。
f:id:bluebirdofoz:20170803034755j:plain

Sceneタブの視点を遠ざけていくと、巨大な円形のSkyboxオブジェクトがあることが分かります。
f:id:bluebirdofoz:20170803034806j:plain

アプリが開始すると、SpatialMapping のメッシュが視界を覆います。
SpatialMapping のメッシュに穴を開けて、その奥にある Skybox オブジェクトのメッシュを覗き見るという仕組みです。
f:id:bluebirdofoz:20170803034839j:plain

視界を遮る SpatialMapping のシェーダと、そこに窓を開ける Window オブジェクトのシェーダを読み解きます。
まずは、視界を遮る SpatialMapping のシェーダです。
・Wall.shader

Shader "HoloLens/Wall"
{
  Properties
  {
    _Mask("Mask", Int) = 1
  }
  SubShader
  {
    // タグ付け
    Tags 
    { 
      // "RenderType(レンダータイプ)"を"Opaque(不透明)"に指定
      "RenderType" = "Opaque" 
      // "Queue(描画順)"を"Geometry(デフォルト)-1"に指定
      // Background→Geometry→AlphaTest→Transparent→Overlayの順で描画される。
      // つまり通常オブジェクトより背景に近いシェーダとして設定している
      "Queue" = "Geometry-1"
    }
    CGINCLUDE
    // "UnityCG.cginc"をインクルード
    #include "UnityCG.cginc"

    // v2f構造体の定義
    // v2fという名前は慣例で Vertex To Fragmentの略
    // 頂点シェーダからフラグメントシェーダに複数の値を渡す時に利用する
    struct v2f
    {
      // float4 SV_POSITION  MVP 変換後の座標
      float4 vertex : SV_POSITION;
      // VRのシングルパスステレオレンダリングのマクロ
      UNITY_VERTEX_OUTPUT_STEREO
    };
    // 頂点シェーダ
    v2f vert(appdata_base v)
    {
      // 返り値として利用するv2f構造体の変数を作成
      v2f o;
      // インスタンス ID がシェーダー関数にアクセス可能になる
      UNITY_SETUP_INSTANCE_ID(v);
      // VR向けの頂点シェーダーへの変換を行う
      UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
      // 同次座標において、オブジェクト空間からカメラのクリップ空間へ点を変換する
      o.vertex = UnityObjectToClipPos(v.vertex);
      // 構造体を返す
      return o;
    }
    // フラグメントシェーダ
    fixed4 frag(v2f i) : COLOR
    {
      // 描画するカラーを返却する
      // 必ず 0 を返却する
      // ただし ColorMask 0 が設定されているため、カラーチャネルは書き込まれない
      return 0;
    }
    ENDCG

    Pass
    {
      // カラーチャネルの書き込みを設定
      // ColorMask 0 ですべてのカラーチャネルのレンダリングを無効化
      ColorMask 0
      // ZWrite:デプスバッファに書き込みするか制御
      // 不透明なオブジェクトを描画する場合、On
      ZWrite On
      // ZTest:デプステストの実行方法
      // LEqual:既に描画されているオブジェクトと距離が等しいか、より近い場合に描画する
      //        それより遠い場合はオブジェクトで隠す
      ZTest LEqual

      // Stencil:ステンシルバッファはピクセルマスクごとにピクセルを保存や廃棄することを目的とする
      // ステンシルバッファは、通常、1 ピクセルあたり 8 ビットの整数である
      Stencil 
      {
        // バッファに_Maskの 1 の整数が書き込まれる
        Ref [_Mask]
        // Comp:関数はバッファの現在の内容と基準値の比較に使用される
        // NotEqual:ピクセルのレファレンス値がバッファの値と等しくない場合のみレンダリングする
        Comp NotEqual
      }

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #pragma target 5.0
      #pragma only_renderers d3d11
      ENDCG
    }
  }
}

次に、壁に穴を開ける Window のシェーダです。
・Window.shader

Shader "HoloLens/Window"
{
  Properties
  {
    _Mask ("Mask", Int) = 1
  }
  SubShader
  {
    // タグ付け
    Tags 
    { 
      // "RenderType(レンダータイプ)"を"Opaque(不透明)"に指定
      "RenderType" = "Opaque" 
      // "Queue(描画順)"を"Geometry(デフォルト)-2"に指定
      // つまり先ほどの Wall オブジェクトより先駆けて描画する
      "Queue" = "Geometry-2"
    }
    CGINCLUDE
    // "UnityCG.cginc"をインクルード
    #include "UnityCG.cginc"

    // v2f構造体の定義
    // v2fという名前は慣例で Vertex To Fragmentの略
    // 頂点シェーダからフラグメントシェーダに複数の値を渡す時に利用する
    struct v2f
    {
      // float4 SV_POSITION  MVP 変換後の座標
      float4 vertex : SV_POSITION;
      // VRのシングルパスステレオレンダリングのマクロ
      UNITY_VERTEX_OUTPUT_STEREO
    };
    // 頂点シェーダ
    v2f vert(appdata_base v)
    {
      // インスタンス ID がシェーダー関数にアクセス可能になる
      UNITY_SETUP_INSTANCE_ID(v);
      // 返り値として利用するv2f構造体の変数を作成
      v2f o;
      // 同次座標において、オブジェクト空間からカメラのクリップ空間へ点を変換する
      o.vertex = UnityObjectToClipPos(v.vertex);
      // VR向けの頂点シェーダーへの変換を行う
      UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
      // 構造体を返す
      return o;
    }
    // フラグメントシェーダ
    fixed4 frag (v2f i) : SV_Target
    {
      // 描画するカラーを返却する
      // 必ず 0 を返却する
      // ただし ColorMask 0 が設定されているため、カラーチャネルは書き込まれない
      return 0;
    }
    ENDCG

    Pass
    {
      // カラーチャネルの書き込みを設定
      // ColorMask 0 ですべてのカラーチャネルのレンダリングを無効化
      ColorMask 0
      // ZWrite:デプスバッファに書き込みするか制御
      // 透明なオブジェクトを描画する場合、Off
      ZWrite Off
      // Stencil:ステンシルバッファはピクセルマスクごとにピクセルを保存や廃棄することを目的とする
      // ステンシルバッファは、通常、1 ピクセルあたり 8 ビットの整数である
      Stencil 
      {
        // バッファに_Maskの 1 の整数が書き込まれる
        Ref [_Mask]
        // Comp:関数はバッファの現在の内容と基準値の比較に使用される
        // Always:常にステンシルテストをパスさせる
        Comp Always
        // Pass:ステンシルテスト(及びデプステスト)をパスした場合、バッファの内容をどうするか決める
        // Replace:リファレンス値をバッファに書き込む
        Pass Replace
      }

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      ENDCG
    }
  }
}

要は Window.shader が先駆けてステンシルバッファに 1 を書き込み。
その後、Wall.shader がステンシルバッファに 1 が書き込まれていない箇所のみ、デプスバッファを設定して背景の Skybox の描画を遮蔽しています。

以下の記事も参考になります。
tips.hecomi.com

次回は、方法2の動作と仕組みについて理解します。
bluebirdofoz.hatenablog.com