夜風のMixedReality

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

HoloLensの視野角を可視化するウィンドウを作成する。 その① ベースシェーダーを作る

本日はHoloLens枠です。

HoloLens 2のアプリケーションを初めての方に体験してもらう際にはデバイスの装着位置がずれている場合視野角の問題でホログラムを見ることができず、途切れて見えてしまうということがあります。

そのためアテンド側、アプリケーション側のいずれかでユーザーが正しい位置にデバイスを装着できているかを判断し、もし正しい位置に装着されていない場合はデバイスを上下させ正しく着用させるように促すUXが必要になる場合があります。

今回はHoloLens 2のアイキャリブレーションの際に表示される視野角に沿って表示されるような枠をアプリケーションに組み込んでいきます。

〇環境

今回は以下の環境で実行していきます。

 ・Unity2021.3.5f1

 ・Universal RP

 ・Windows11

 ・Microsoft HoloLens 2

なおMRTK3を使用していますが、MRTKv2でも同名のコンポーネントを使用できます。

〇頭に沿って追従する板を実装する

視野角を可視化するウィンドウはいくつかの方法で実現できますが、今回は頭に追従する板とそこに描画するシェーダーで実装していきます。

頭に沿って追従する板はUnityのプリミティブのQuadをMRTK XR RigCamera OffsetMain Camera内に配置します。

これによって頭の動きに追従していたが追従するようになります。

〇視野角を可視化するシェーダー

①任意のマテリアルを作成しQuadにアタッチします。

②次のようなシェーダーを作成します。

シェーダー

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Width("width",float) = 1
        _Height("height",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
           float _Width;
            float _Height;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);

                 float2 d = abs(i.uv * 2 - 1) - float2(_Width, _Height);
                 d = 1 - d / fwidth(d);
                col *= saturate(min(d.x, d.y));
                return col;
            }
            ENDCG
        }
    }
}

ここで追加したのはフラグメントシェーダーの処理で、シェーダー内部でwidthとheightを変数とする長方形を描画できるようになります。

③フラグメントシェーダー部で長方形の出力色を反転させます。

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);

                 float2 d = abs(i.uv * 2 - 1) - float2(_Width, _Height);
                 d = 1 - d / fwidth(d);
                col *=  saturate(1-min(d.x, d.y));//1-min()に変更
                return col;
            }

シェーダー内でのインバート(反転)処理はsaturate(1-x)を使用します。

1-xだけだと出力色が負の値になる場合があるためsaturate()で正の値になるようにしています。

これによって中央が黒、周囲が白になります。

中央の黒い部分を透明に、白い部分を枠として使用します。

④くりぬきを行うためにAlphaToMask Onを定義します。

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Width("width",float) = 1
        _Height("height",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        AlphaToMask On//追加
        Pass
        {
        ・・・

これによって中央がくりぬかれます。

⑤最後にプロパティ側から枠を調整します。

以上でベースのシェーダーが完成しました。

ここからHoloLensのUXに合わせて使用できるように調整していきます。

〇シェーダー全文

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Width("width",float) = 1
        _Height("height",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        AlphaToMask On//追加
        Pass
        {
            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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
           float _Width;
            float _Height;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);

                 float2 d = abs(i.uv * 2 - 1) - float2(_Width, _Height);
                 d = 1 - d / fwidth(d);
                col *=  saturate(1-min(d.x, d.y));
                return col;
            }
            ENDCG
        }
    }
}