夜風のMixedReality

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

MRTK StanderdShaderを読み解く その② Pass{} MixedRealityShaderUtils.cginc

本日はMRTKのShader学習枠です。

MRTKにはHoloLensなどのMxiedRealityデバイスに特化したShaderが提供されています。

前回その中の標準的なShaderであるMRTK StandardShaderとは何かを学習しました。

今回は詳しく中身を見ていきます。

〇最小限構成のMRTK StandardShader

//ここではMRTKStandardShaderをコピーしてresearchと名付けています。 
Shader "Custom/MRTKStandardShaderResearch"
{
Properties
{
  _Color("Color", Color) = (1.0, 1.0, 1.0, 1.0)
}

SubShader
        {
   Pass
            {
                Name "Main"
                Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
                LOD 100

                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"
                #include "UnityStandardConfig.cginc"
                #include "UnityStandardUtils.cginc"
                #include "MixedRealityShaderUtils.cginc"


            struct appdata_t
            {
                float4 vertex : POSITION;

                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 position : SV_POSITION;

                UNITY_VERTEX_OUTPUT_STEREO

            };
             fixed4 _Color;
         

            v2f vert(appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                float4 vertexPosition = v.vertex;


                o.position = UnityObjectToClipPos(vertexPosition);

                return o;
            }

            fixed4 frag(v2f i, fixed facing : VFACE) : SV_Target
            {
                fixed4 albedo = fixed4(1.0, 1.0, 1.0, 1.0);
                albedo *= _Color;
                fixed4 output = albedo;

                return output;
            }

            ENDCG
        }
        }
            Fallback "Hidden/InternalErrorShader"
}

〇Passブロック

Pass{
      Name "Main"
                Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
                LOD 100

                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"
                #include "UnityStandardConfig.cginc"
                #include "UnityStandardUtils.cginc"
                #include "MixedRealityShaderUtils.cginc"
                   ...
               ENDCG
        }

 Mainという名前のパスです。

TagsではRenderTypeとしてOpaque(不透明)、LightModeとしてForwardBaseが指定されています。

docs.unity3d.com

LODとはLevel of Detailの意でハードウェアのスペック事に使用するパスを分けています。

これと似た仕組みとしてFallback "ShaderName"というものがありますが、こちらは『このShaderが実行できなかった場合"ShaderName"を実行する』という意味です。

LODを使用する場面として例えば、このShaderがAというハードウェアで実行できるとします。 しかしそのAはそこまでスペックが高くなく、ぎりぎり描画できるということがあります。

このように実行はできるけどスペック的に最適な描画が行えない場合LODを使用して別のPassを実行します。

docs.unity3d.com

MRTKStandardShaderの場合LODは100なので実行するための敷居が低いと言えます。

〇頂点シェーダーとフラグメントシェーダー

               #pragma vertex vert
                #pragma fragment frag

それぞれvertという名前で頂点シェーダーを、fragという名前でフラグメントシェーダーを用いるという意味です。

〇組み込み型ビルドインシェーダー

               #include "UnityCG.cginc"
                #include "UnityStandardConfig.cginc"
                #include "UnityStandardUtils.cginc"
                #include "MixedRealityShaderUtils.cginc"

inclubeするビルドインシェーダーを指定しています。

ここではUnityで提供されるビルドインシェーダーのほかにMixedRealityShaderUtils.csincというMixedRealityToolkitで提供されているビルドインシェーダーを用いています。

github.com

これはMRTKのStandardAssets/Shader内にあります。

#ifndef MRTK_SHADER_UTILS
#define MRTK_SHADER_UTILS

#if defined(_CLIPPING_PLANE)
inline float PointVsPlane(float3 worldPosition, float4 plane)
{
    float3 planePosition = plane.xyz * plane.w;
    return dot(worldPosition - planePosition, plane.xyz);
}
#endif

#if defined(_CLIPPING_SPHERE)
inline float PointVsSphere(float3 worldPosition, float4 sphere)
{
    return distance(worldPosition, sphere.xyz) - sphere.w;
}
#endif

#if defined(_CLIPPING_BOX)
inline float PointVsBox(float3 worldPosition, float3 boxSize, float4x4 boxInverseTransform)
{
    float3 distance = abs(mul(boxInverseTransform, float4(worldPosition, 1.0))) - boxSize;
    return length(max(distance, 0.0)) + min(max(distance.x, max(distance.y, distance.z)), 0.0);
}
#endif


#endif

MixedRealityShaderUtils.cgincでは定義条件によってコンパイルしたり、しなかったりを行う条件コンパイルを行っています。

#ifndef は定義されていない場合に、#ifndef~#endif間のコードが有効となり、コンパイルが行われます。

つまりMRTK_SHADER_UTILSが定義されていない場合MRTK_SHADER_UTILSの定義を行います。

#if defined(_CLIPPING_PLANE)
inline float PointVsPlane(float3 worldPosition, float4 plane)
{
    float3 planePosition = plane.xyz * plane.w;
    return dot(worldPosition - planePosition, plane.xyz);
}
#endif

_CLIPPING_PLANEが定義されている場合PointVsPlane関数が処理されます。

同様に_CLIPPING_SPHEREが定義されている場合PointVsSphere関数を実行します、

#if defined(_CLIPPING_SPHERE)
inline float PointVsSphere(float3 worldPosition, float4 sphere)
{
    return distance(worldPosition, sphere.xyz) - sphere.w;
}
#endif

また、_CLIPPING_BOXが定義されている場合PointVsBox関数を処理します。

#if defined(_CLIPPING_BOX)
inline float PointVsBox(float3 worldPosition, float3 boxSize, float4x4 boxInverseTransform)
{
    float3 distance = abs(mul(boxInverseTransform, float4(worldPosition, 1.0))) - boxSize;
    return length(max(distance, 0.0)) + min(max(distance.x, max(distance.y, distance.z)), 0.0);
}
#endif

変数の名称からしてこれはClippingBorderにかかわる機能のようです。