夜風のMixedReality

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

HoloLensのSpatialMeshに影を描画する

本日はHoloLensの表現実験枠です。

HoloLensではオブジェクトを空間に配置することができますが、一般的に影の描画を行うことは稀です。

これには次のような理由があります。

①光学シースルーの特性上黒を描画できないため

②ライト処理は重たい処理であるため

MixedRealityToolkitで提供されているシェーダーも影に関するサポートを行っていません。(MRTK StandardShaderおよびGraphicsTools StandardShader 後者は筆者が現在影の機能を開発中です。)

 しかしオブジェクト数が少ない状態でパフォーマンスへの影響が少ない場合やMagicLeap2で行う場合などは十分影を描画できる可能性があります。

 今回はSpatilalMeshのシェーダーを改造して、オブジェクトの影を受け取る表現を試してみたいと思います。

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

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

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

qiita.com

〇影の実装

影を受けるシェーダー自体は過去の記事で紹介しています。

redhologerbera.hatenablog.com

ただし、一般的に影は黒く描画されます。

SpatialMeshではHoloLensが黒を描画できない特性を利用していますので、過去の記事のまま行ったところもともと黒く描画されているオブジェクトに対して黒い影を落とすという結局黒で何も描画されないといった状態になってしまいます。

 そこで今回は影の色をグレーにしてみました。

 この実装を紹介します。

 フラグメントシェーダーで今回は影の色を変化できるようにしています。

  float4 col = float4(1,1,1,1);
                Light lt = GetMainLight(input.shadowCoord);

                float strength = dot(lt.direction, input.normalWS);
                float4 lightColor = float4(lt.color, 1)*(lt.distanceAttenuation * lt.shadowAttenuation);
               // return col* lightColor*strength;
                col =  abs(1-saturate(lightColor.r));
                col *= _ShadowColor;
                return col;

もともとlightColorの状態で出力される影の色は黒です。

  float4 lightColor = float4(lt.color, 1)*(lt.distanceAttenuation * lt.shadowAttenuation);

考え方としてこの全体の色を反転させ、影ではない場所を黒、影を白に変換します。

これを行っているのが次の一行です。

 col =  abs(1-saturate(lightColor.r));

ここでは1から元の値を引いて絶対値を取得することで反転させています。

lightColor.rとRedのみを取得しているのは単純にlightColorの値はグレースケールをとるため単純化しています。

ここにプロパティで定義した_ShadowColorの色を掛け合わせています。

ここで黒=0のため何をかけても黒になります。反対に白は1のため掛け合わせた_ShadowColorの値が影の色になります。

これで影をグレーにしています。

〇実機で確認

youtu.be

実機で正常に作動を確認できました。

このシェーダーは一つだけ問題があります。

通常セルフシャドウ(自分自身に影があたる)によって貫通した影というものは見えないようになりますが、このシェーダーではSpatialMesh自身の影は描画していません。

そのため貫通した影が見えてしまうという問題はあります。

〇ソースコード

Shader "Unlit/Spatialhole"
{
    Properties
    {
        _ShadowColor("ShadowColor",color) = (1,1,1,1)
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
            "Queue"="Geometry-1"
        }

        ZWrite On
        ZTest LEqual

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include  "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            struct appdata
            {
                float4 vertex : POSITION;
                half3 normal: NORMAL;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 position : SV_POSITION;
                float3 normalWS : TEXCOORD1;
                float4 shadowCoord : TEXCOORD3;
                UNITY_VERTEX_OUTPUT_STEREO
            };
            float4 _MainColor;
            float4  _ShadowColor;
            v2f vert(appdata input)
            {
                v2f output;
                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

                output.position = TransformObjectToHClip(input.vertex);
                 VertexNormalInputs normal = GetVertexNormalInputs(input.normal);
                output.normalWS = normal.normalWS;
                VertexPositionInputs vertexInput = GetVertexPositionInputs(input.vertex.xyz);//追加
                output.shadowCoord = GetShadowCoord(vertexInput);//追加
                return output;
            }

            float4 frag(v2f input) : SV_Target
            {
               float4 col = float4(1,1,1,1);
                Light lt = GetMainLight(input.shadowCoord);

                float strength = dot(lt.direction, input.normalWS);
                float4 lightColor = float4(lt.color, 1)*(lt.distanceAttenuation * lt.shadowAttenuation);
               // return col* lightColor*strength;
                col =  abs(1-saturate(lightColor.r));
                col *= _ShadowColor;
                return col;
            }
            ENDHLSL
        }
    }
}