夜風のMixedReality

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

MRTK HandTrianglesShaderの中身を読み解く その④ ジオメトリシェーダーを読み解く 序

本日はMRTKのHandTrianglesShaderの勉強枠です。

前回ジオメトリシェーダーの扱いを学びました。

今回MRTKのHandTrianglesShaderのジオメトリシェーダーを読み解いていきます。 長いのでその① 序です。

redhologerbera.hatenablog.com

  [maxvertexcount(Geo_Max_Out_Vertices)]
    void geometry_main(triangle VertexOutput vxIn[3], inout TriangleStream<FragmentInput> triStream)
    {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(vxIn[0]);
        //huxEye = _WorldSpaceCameraPos;
        //workaround for Unity's auto updater in 5.6
        vxOutCount=0;
        stripCount=0;
        stripVxCount[0]=0;

        float2 Out_UV_1_Q154;
        float2 Out_UV_2_Q154;
        float2 Out_UV_3_Q154;
        Flip_V_For_Hydrogen_B154(_Flip_V_,vxIn[0].uv,vxIn[1].uv,vxIn[2].uv,Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154);

        float Result_Q149;
        AutoPulse_B149(_Pulse_,_Auto_Pulse_,_Period_,_Time.y,Result_Q149);

        // To_XYZW
        float X_Q166;
        float Y_Q166;
        float Z_Q166;
        float W_Q166;
        X_Q166=_Pulse_Origin_.x;
        Y_Q166=_Pulse_Origin_.y;
        Z_Q166=_Pulse_Origin_.z;
        W_Q166=_Pulse_Origin_.w;

        float2 Average_Q153;
        float Wrist_1_Q153;
        float Wrist_2_Q153;
        float Wrist_3_Q153;
        Average_B153(Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154,vxIn[0].posWorld,vxIn[1].posWorld,vxIn[2].posWorld,_Wrist_Fade_Start_,_Wrist_Fade_End_,Average_Q153,Wrist_1_Q153,Wrist_2_Q153,Wrist_3_Q153);

        // From_XY
        float2 Vec2_Q167 = float2(X_Q166,Y_Q166);

        float Result_Q150;
        Cell_Noise_2D_B150(Average_Q153,_Pulse_Noise_Frequency_,111,Result_Q150);

        // Distance2
        float Distance_Q159 = distance(Average_Q153,Vec2_Q167);

        float Result_Q151;
        Cell_Noise_2D_B151(Average_Q153,_Pulse_Noise_Frequency_,333,Result_Q151);

        float Transition_Q160;
        bool FadingOut_Q160;
        float Saturation_Q160;
        float Fade_Color_Q160;
        Pulse_B160(Distance_Q159,Result_Q150,_Pulse_Enabled_,Result_Q149,_Pulse_Width_,_Pulse_Outer_Size_,_Pulse_Lead_Fuzz_,_Pulse_Tail_Fuzz_,_Pulse_Vary_,_Pulse_Color_Width_,Transition_Q160,FadingOut_Q160,Saturation_Q160,Fade_Color_Q160);

        float4 Color_Q157;
        #if defined(USE_ALBEDO_TEXTURE)
          Pt_Sample_Texture_B157(Average_Q153,Result_Q151,_Color_Map_,_Vary_UV_,1,Color_Q157);
        #else
          Color_Q157 = float4(1,1,1,1);
        #endif

        float4 Extra1_1_Q163;
        float4 Extra1_2_Q163;
        float4 Extra1_3_Q163;
        float3 Nearest_P_Q163;
        Find_Nearest_B163(Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154,Vec2_Q167,Transition_Q160,FadingOut_Q160,vxIn[0].posWorld,vxIn[1].posWorld,vxIn[2].posWorld,Extra1_1_Q163,Extra1_2_Q163,Extra1_3_Q163,Nearest_P_Q163);

        // Color
        float4 Result_Q161;
        float k = max(Color_Q157.r,max(Color_Q157.g,Color_Q157.b))*_Desaturated_Intensity_;
        Result_Q161 = lerp(float4(k,k,k,1),Color_Q157,float4(Saturation_Q160,Saturation_Q160,Saturation_Q160,Saturation_Q160))*(1.0-_Vary_Color_*Result_Q150)*Fade_Color_Q160;
        Result_Q161.rgb *= _Intensity_;
        
        float3 Q0_Q164;
        float3 Q1_Q164;
        float3 Q2_Q164;
        Fly_B164(vxIn[0].posWorld,vxIn[1].posWorld,vxIn[2].posWorld,Transition_Q160,_Max_Hover_,_Max_In_Angle_,_Max_Out_Angle_,Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154,Vec2_Q167,Nearest_P_Q163,FadingOut_Q160,Q0_Q164,Q1_Q164,Q2_Q164);

        bool Next_Q177;
        Emit_Triangle_B177(false,Q0_Q164,Q1_Q164,Q2_Q164,Extra1_1_Q163,Extra1_2_Q163,Extra1_3_Q163,Result_Q161,Wrist_1_Q153,Wrist_2_Q153,Wrist_3_Q153,Transition_Q160,Next_Q177);

        bool Root = Next_Q177;


        int vxix=0;
        int strip=0;
        [unroll]
        while (strip<stripCount) {
            int i=0;
            [unroll]
            while (i<stripVxCount[strip]) {
                //UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(vxOut[vxix]);
                UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(vxIn[0],vxOut[vxix]);

                triStream.Append(vxOut[vxix]);
                i+=1; vxix+=1;
            }
            triStream.RestartStrip();
            strip+=1;
        }
    }

〇shaderシンタックス

  [maxvertexcount(Geo_Max_Out_Vertices)]
    void geometry_main(triangle VertexOutput vxIn[3], inout TriangleStream<FragmentInput> triStream){
    }

triangle VerexOutput vxin[3]はVertexOutputから3つの頂点をvxInのリストに入れるという意味です。

inout TriangleStream triStreamは処理されたものはFragmentInputにポリゴンとして値を返すよ!という意味です。

[maxvertexcount(x)]では最大xの頂点数を受け取るよ!という意味になります。Geo_Max_Out_Verticesは146~148行目で記述されています。

    #ifndef Geo_Max_Out_Vertices
    #define Geo_Max_Out_Vertices 16
    #endif

でGeo_Max_Out_Verticesが定義されていない場合16を代入することとあります。

       UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(vxIn[0]);
        //huxEye = _WorldSpaceCameraPos;
        //workaround for Unity's auto updater in 5.6
        vxOutCount=0;
        stripCount=0;
        stripVxCount[0]=0;

UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEXはシングルレンダリングパスにかかわるもので左右のどちらの目の処理を行うかを見るデバッグ等で使用するもののようです。

頂点シェーダーで記述した

        UNITY_SETUP_INSTANCE_ID(v);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
        UNITY_TRANSFER_INSTANCE_ID(v, o);

の情報をもとにGPU がどの目にレンダリングするかを調べるために使用されます。

●Flip_V_For_Hydrogen_B154
 Flip_V_For_Hydrogen_B154(_Flip_V_,vxIn[0].uv,vxIn[1].uv,vxIn[2].uv,Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154);

Flip_V_For_Hydrogen_B154()関数に値を代入しています。

Flip_V_For_Hydrogen_B154()関数は471行目で記述されています。

  void Flip_V_For_Hydrogen_B154(
        bool Flip_V,
        float2 UV_1,
        float2 UV_2,
        float2 UV_3,
        out float2 Out_UV_1,
        out float2 Out_UV_2,
        out float2 Out_UV_3    )
    {
        Out_UV_1 = Flip_V ? float2(UV_1.x,1.0-UV_1.y) : UV_1;
        Out_UV_2 = Flip_V ? float2(UV_2.x,1.0-UV_2.y) : UV_2;
        Out_UV_3 = Flip_V ? float2(UV_3.x,1.0-UV_3.y) : UV_3;
        
    }

この引数にそれぞれ値が代入されます。一つ一つ見ていくと

・bool Flip_V = Propertiesの[Toggle] Flip_V("Flip V", Float) = 0

・float2 UV_1=vxIn[0](一つ目の頂点ベクトル)

・float2 UV_2 =vxIn[1](2つ目の頂点ベクトル)

・float2 UV_3=vxIn[2](3つ目の頂点ベクトル)

・out float2 Out_UV_1=uv,Out_UV_1_Q154

・ out float2 Out_UV_2 =Out_UV_2_Q154

・ out float2 Out_UV_3 =Out_UV_3_Q154

が代入されます。

Flip_V_For_Hydrogen_B154関数内ではOut_UV_X = Flip_V ? float2(UV_X.x,1.0-UV_X.y) : UV_Xと処理されています。(Xはそれぞれ1,2、3)

これはFlip_VがオンであればOut_UV_Xに頂点ベクトルのx成分からy成分を引いたものを、オフであればそのまま頂点ベクトルを代入しなさいという意味です。

Out_UV_XはジオメトリシェーダーのFlip_V_For_Hydrogen_B154()の前に宣言されています。

        float2 Out_UV_1_Q154;
        float2 Out_UV_2_Q154;
        float2 Out_UV_3_Q154;
        Flip_V_For_Hydrogen_B154(_Flip_V_,vxIn[0].uv,vxIn[1].uv,vxIn[2].uv,Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154);
●AutoPulse_B149
      float Result_Q149;
        AutoPulse_B149(_Pulse_,_Auto_Pulse_,_Period_,_Time.y,Result_Q149);

次はAutoPulse_B149()関数に飛んでいます。これは549~562行目で記述されています。

   void AutoPulse_B149(
        float Pulse,
        bool Auto_Pulse,
        float Period,
        float Time,
        out float Result    )
    {
        
        if (Auto_Pulse) {
            Result = frac(Time/Period);
        } else {
            Result = Pulse;
        }
    }

ここもひとつづつ見ていきます。

・float Pulse=Propertiesで設定されたPulseの値

・bool Auto_Pulse=Propertiesで設定されたAutoPulse

・float Period=Propertiesで設定されたPeriod

・float Time=時間

・out float Result=Result_Q149

が代入されます。

処理を見ていきます。

        if (Auto_Pulse) {
            Result = frac(Time/Period);
        } else {
            Result = Pulse;
        }

Auto_Pulseがオンの場合とオフの場合で分岐があります。

Auto_Pulseがオンの場合Result_Q149に時間を_Periodで割った少数部分が代入されます。

Auto_Pulseがオフの場合Result_Q149にはPulseの値が代入されます。

●_Pulse_Origin
        // To_XYZW
        float X_Q166;
        float Y_Q166;
        float Z_Q166;
        float W_Q166;
        X_Q166=_Pulse_Origin_.x;
        Y_Q166=_Pulse_Origin_.y;
        Z_Q166=_Pulse_Origin_.z;
        W_Q166=_Pulse_Origin_.w;

ここではPropertiesのPulse_Originで設定されたx,y,z,wの値がそれぞれ〇Q166として代入されています。

●Average_Q153
 float2 Average_Q153;
        float Wrist_1_Q153;
        float Wrist_2_Q153;
        float Wrist_3_Q153;
        Average_B153(Out_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154,vxIn[0].posWorld,vxIn[1].posWorld,vxIn[2].posWorld,_Wrist_Fade_Start_,_Wrist_Fade_End_,Average_Q153,Wrist_1_Q153,Wrist_2_Q153,Wrist_3_Q153);

次はAverage_B153()関数へ飛んでいます。Average_B153()は401~421行で記述されています。

   void Average_B153(
        float2 A2,
        float2 B2,
        float2 C2,
        float3 P1,
        float3 P2,
        float3 P3,
        float Wrist_Start,
        float Wrist_End,
        out float2 Average,
        out float Wrist_1,
        out float Wrist_2,
        out float Wrist_3    )
    {
        Average = (A2 + B2 + C2) * (1.0/3.0);
        Wrist_1 = saturate((A2.y-Wrist_Start)/(Wrist_End-Wrist_Start));
        Wrist_2 = saturate((B2.y-Wrist_Start)/(Wrist_End-Wrist_Start));
        Wrist_3 = saturate((C2.y-Wrist_Start)/(Wrist_End-Wrist_Start));
        
    }

AverageはOut_UV_1_Q154,Out_UV_2_Q154,Out_UV_3_Q154の平均値をとります。これは3つの位置ベクトルによる面の重心を意味します。

 Wrist_XにはOut_UV_X_Q154のy成分からWrist_Startを引いたものをWrist_EndからWrist_Startを引いたWristの幅で割った値が代入されます。この値はsaturate関数によって0~1の間に限定されます。

〇ここまでのひとまとめ

ジオメトリシェーダー内の処理は長いためここで一度ここまでの処理の流れを見ていきます。

①geometry_mainは最大Geo_Max_Out_Vertices数の頂点数を受け取ります。

②VertexOutputから3つの頂点をvxInのリストに入れいれジオメトリシェーダーで処理されたものはFragmentInputにポリゴンとして値を返される。

③_FlipがMaterialでオンになっている場合頂点ベクトルのxからyを減算したものが、オフの場合はそのまま頂点ベクトルがOut_UV_X_Q154の値に代入されます。

AutoPulseがMaterialでオンになっている場合Result_Q149に時間をPeriodで割った少数部分オフの場合_Pulseが代入されます。

Q166にはプロパティで指定されているPulse_Origin_の各成分が代入されます。

⑥Average_B153で③で与えられたOut_UV_X_Q154の値(=頂点ベクトル)の平均を求めAverageとして代入します。

⑦また、Out_UV_X_Q154のy成分からWrist_Startを引いたものをWrist_EndからWrist_Startを引いたWristの幅で割った値がWrist_として代入されます。