夜風のMixedReality

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

MRGT GraphicsToolsStandardShaderに影を受け取る機能を追加する。 その③URP用の処理の実装

本日はMRGT枠です。

前回に引き続きGraphicsToolsStandardShaderで影を受け取る機能を追加します。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

〇MRGTとは?

MixedRealityGraphicsTools(MRGT)はMicrosoftによってオープンソースな形で開発、提供されているMixedRealityデバイス向けのSDKである第三世代のMixedRealityToolkit(MRTK3)で提供されるグラフィック関連のパッケージを指します。

 MRTKのコア機能に当たるGraphicsToolsStandardShaderは多くの機能をサポートしていますが、もともとがHoloLensで使用される想定であったことと軽量化のためにほかのオブジェクトとの影の相互関係の機能がありません。

github.com

 今回は影を受けるようにしていっています。

〇Varyingsの処理の実装

 GraphicsToolsStandardShaderでは頂点シェーダーで使用される入力値を格納する構造体名がVaryingsとして定義されています。

struct Varyings
{
    float4 position : SV_POSITION;
 ・・・
#if defined(_RECEIVESHADOW)
    half4 shadowCoord : TEXCOORD4;//追加
#endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

ここにTEXCOORD4のhalf4型としてshadowCoordを定義します。

なおここで_RECEIVESHADOWが有効な場合コンパイルされるように分岐処理を記述しています。

これはプロパティブロックで[Toggle("変数名")]で定義した変数をShaderFeatureの機能を使用することで実装しています。

Properties{
  ・・・
        [Toggle(_RECEIVESHADOW)] _ReceiveShadow("ReceiveShadow",Float) =0.0
    ・・・
}

・・・
#pragma shader_feature_local _RECEIVESHADOW

・・・

〇頂点シェーダーでの処理の実装

頂点シェーダーでは先に定義したVaryings構造体に格納するデータを処理します。

#if defined(_RECEIVESHADOW)
#if defined(_URP)
    VertexPositionInputs vertexInput = GetVertexPositionInputs(vertexPosition.xyz);
    output.shadowCoord = GetShadowCoord(vertexInput);
#endif 
#endif

ここはURPでの影の取得と同じ方法で行っています。

〇フラグメントシェーダー

フラグメントシェーダーではGTMainLight lightにライト情報を格納しています。

これを700行目あたりのGTGetMainLight()で行われています。

ここに引数にinput.shadowCoordを加えたGTGetMainLight()の関数を条件コンパイルで定義します。

   // Direct lighting.
#if defined(_RECEIVESHADOW)
    GTMainLight light = GTGetMainLight(input.shadowCoord);
#else
    GTMainLight light = GTGetMainLight();
    #endif

次にGraphicsToolsLighting.hlslで定義されるGTGetMainLight()側に引数の定義が存在しないため定義を行います。

こちらも条件コンパイルで処理を定義している場合のみ分岐するようにします。

#if defined(_RECEIVESHADOW)
GTMainLight GTGetMainLight(float4 shadowCoord)
{
#else
    GTMainLight GTGetMainLight()
    {
#endif
 ・・・
}

これでマテリアルプロパティでReceiveShadowを有効にしている場合(_RECEIVESHADOWが定義されている場合)GTGetMainlight()の引数としてデータを渡せるようになりました。

次にGetMainLightの処理ですが、処理を行う前にLight情報を格納するGTMainLight構造体にもshadowAttenuationを定義します。

struct GTMainLight
{
    half3 direction;
    half3 color;
    #if defined(_RECEIVESHADOW)
    half4 shadowAttenuation;
#endif
};

これでGTMainLight.shadowAttenuationが使用できるようになります。

次にRealtimeLighting.hlslGetMainlight()の引数としてshadowCoordを渡します。

#if defined(_RECEIVESHADOW)
        light.shadowAttenuation = GetMainLight(shadowCoord).shadowAttenuation;
#endif
#else

これでGTMainLight.shadowAttenuationにほかのオブジェクトによる影情報が格納されるようになりました。

最後にGraphicsToolsStandardProgram.hlslへもどり_NON_PHOTOREALISTICが有効な場合、無効な場合でそれぞれに対して_ReceiveShadowを有効化している状態の分岐処理を記述します。

    // Non Photorealistic
#if defined(_NON_PHOTOREALISTIC)
#if defined(_RECEIVESHADOW)
    output.rgb += GTLightingNonPhotorealistic(brdfData, light.color, light.direction, worldNormal, worldViewDir,light.shadowAttenuation);
#else
    output.rgb += GTLightingNonPhotorealistic(brdfData, light.color, light.direction, worldNormal, worldViewDir);
#endif
#else
#if defined(_RECEIVESHADOW)
    output.rgb += GTLightingPhysicallyBased(brdfData, light.color, light.direction, worldNormal, worldViewDir, light.shadowAttenuation);
#else
    output.rgb += GTLightingPhysicallyBased(brdfData, light.color, light.direction, worldNormal, worldViewDir);
#endif
    #endif

ライトの処理はGTLightingPhysicallyBased()(NPRの場合は GTLightingNonPhotorealistic())で行われています。

こちらも GTLightingPhysicallyBased()およびGTLightingNonPhotorealistic()に引数を追加します。

#if defined(_RECEIVESHADOW)
    half3 GTLightingPhysicallyBased(GTBRDFData brdfData,
        half3 lightColor, half3 lightDirectionWS,
        half3 normalWS, half3 viewDirectionWS ,half4 shadowAttenutation)
    #else
half3 GTLightingPhysicallyBased(GTBRDFData brdfData,
    half3 lightColor, half3 lightDirectionWS,
    half3 normalWS, half3 viewDirectionWS)
#endif
{
  ・・・ 
}

#if defined(_RECEIVESHADOW)
    half3 GTLightingNonPhotorealistic(GTBRDFData brdfData,
    half3 lightColor, half3 lightDirectionWS,
    half3 normalWS, half3 viewDirectionWS,half4 shadowAttenutation)

    #else
half3 GTLightingNonPhotorealistic(GTBRDFData brdfData,
half3 lightColor, half3 lightDirectionWS,
half3 normalWS, half3 viewDirectionWS)
#endif
{
  ・・・
}

最後に_ReceiveShadowを有効化している場合の返り値にそれぞれShadowAttenutationを掛け合わせます。

#if defined(_RECEIVESHADOW)
    half3 GTLightingPhysicallyBased(GTBRDFData brdfData,
        half3 lightColor, half3 lightDirectionWS,
        half3 normalWS, half3 viewDirectionWS ,half4 shadowAttenutation)
    #else
half3 GTLightingPhysicallyBased(GTBRDFData brdfData,
    half3 lightColor, half3 lightDirectionWS,
    half3 normalWS, half3 viewDirectionWS)
#endif
{
  ・・・ 
#if defined(_RECEIVESHADOW)
    return brdf * radiance * shadowAttenutation;
#else
    return brdf * radiance;
#endif
}
#if defined(_RECEIVESHADOW)
    half3 GTLightingNonPhotorealistic(GTBRDFData brdfData,
    half3 lightColor, half3 lightDirectionWS,
    half3 normalWS, half3 viewDirectionWS,half4 shadowAttenutation)

    #else
half3 GTLightingNonPhotorealistic(GTBRDFData brdfData,
half3 lightColor, half3 lightDirectionWS,
half3 normalWS, half3 viewDirectionWS)
#endif
{
 ・・・
#if defined(_RECEIVESHADOW)
   return brdf * radiance * shadowAttenutation;
#else
    return brdf *radiance;
#endif
}

以上で影を受け取る機能が完成しました。