夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

HoloLensで床や壁に穴をあける。 その① SpatialMeshに使用されているシェーダーを調べる HoloLensアドベントカレンダー2022年 5日目

本日はHoloLens2の表現枠です。

〇HoloLens 2022年アドベントカレンダー

 HoloLens 2022年アドベントカレンダーはQiita上で私の師であるがち本さんが開催している企画です。

 クリスマスまで毎日記事を埋めていくことが目的で本日は5日目の記事になります。

qiita.com

〇SpatialAwarenessとSpatialMesh

 SpatialAwareness(空間認識)はHoloLensに搭載されたコア的な機能の一つで、周囲の物理環境をスキャンし、それを基に現実空間と重なるように作成されるSpatialMesh(空間メッシュ)によって実空間同様に壁や机によるOcclusionを実現しています。

redhologerbera.hatenablog.com

 今回はこのSpatialMeshを使用して壁に穴をあけていきます。

〇環境

・Microsoft HoloLens 2

・Unity 2022.3.2f1 (UniversalRenderPipeline)

・MixedRealityToolkit GraphicsTools(MRGT)

〇基本的な考え方

SpatialMeshに穴をあける方法としては様々なものがありますが、今回はステンシルを使用していきます。

 ステンシルとはシェーダーによって描画されるピクセルの持つことができる値で、ステンシル値が違うマテリアル同士が上書き描画される場合ステンシル値同士を比較することで様々な処理をすることができます。

 今回はMRGTの基本的なシェーダーGraphicsToolsStandardShader(以下MRGTシェーダー)を使用していきます。

 MRGTシェーダーの特徴としてデフォルトの状態でステンシルの操作が可能になっています。

〇SpatialMeshのシェーダー

 MRGTシェーダでステンシルを使用してSpatialMeshに穴をあけるためにはSpatialMeshに使用されているマテリアル側のステンシル値を知る必要があります。

SpatialMeshに適応されるマテリアルの設定はMRTKのシーン設定を行ったプロジェクトの場合hierarchyウィンドウから[MixedRealityToolkit]オブジェクトを選択しinspectorウィンドウのMRTKのプロファイルから[SpatialAwareness]のプロファイルを確認することで行えます。

DisplaySettingsDisplay Optionに設定されている情報をもとにVisible MaterialもしくはOcclusion Materialからマテリアルが設定されます。

今回の場合はOcclusion Materialに設定されている MRTK_Occlusionが使用されます。

MRTK_Occlusionに使用されているVR/SpatialMapping/OcclusionシェーダーはUnityに組み込みのシェーダーですが次のGitHubからソースを見ることができます。

github.com

シェーダーの処理としては特別なことはしていません。

  struct appdata
            {
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 position : SV_POSITION;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert (appdata input)
            {
                v2f output;
                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

                output.position = UnityObjectToClipPos(input.vertex);
                return output;
            }

            fixed4 frag (v2f input) : SV_Target
            {
                return fixed4(0.0, 0.0, 0.0, 0.0);
            }

特殊なのはシェーダーの属性で、レンダリングの順番になるRenderQueueが1999になっております。 

キューの順番で描画が行われるため通常の不透明オブジェクトである2000よりも早く描画されていることになります。

        Tags { "Queue"="Geometry-1" }

        ZWrite On//深度を使用
        ZTest LEqual 
        ColorMask 0

ZTestは深度バッファによる描画順のテストです、LEqualが指定されており、すでに描画されているオブジェクトよりユーザーとの距離が近い場合に描画されることになります。(近くのものが描画されるデフォルト)

ColorMask 0を指定することで描画を行わず深度バッファのみを書き込んでいることになります。

このマテリアルをUnityのエディタ上で実行した場合次のように描画がおかしくなってしまいます

こういった場合はSkyBoxを切ることでHoloLensのSpatialMeshによるオクリュージョン同様に背部にあるオブジェクトのオクリュージョンを確認できます。

〇MRGTシェーダーでの代用

ステンシルを使用するためにはVR/SpatialMapping/Occlusionシェーダーにステンシルの機能を追加するか別のシェーダーで代用するという必要があります。

今回はMRGTシェーダーで代用していきます。

今回はマテリアルのプロパティを次のように設定しました。

変更点はRendering ModeColor Write MaskAlphaに設定したこととDepth WriteをオンにしてDepthTestLessEqualに指定した点、そしてLightModeをUnlitに設定している点です。

これによってVR/SpatialMapping/Occlusionシェーダーと同等のシェーダーとして機能します。

VR/SpatialMapping/Occlusionシェーダーが軽量であるためMRGTシェーダーも軽量ではあるものの従来よりは処理が若干重たくはなっていますが今回は微々たるものであると判断しています。

以上でSpatialMeshに使用されるシェーダーにステンシルの機能を持たせることができました。

次回は実際にエディタ上で穴をあけていきます。