2020年12月4日金曜日

C#からCgに行列を受け渡すとどうなるのか?

ややこしいUnityの座標系の確認方法」シリーズの一つ「Unityシェーダでのベクトルと行列の掛け算の仕組み」で Cg単体だと関数mul()によって行列とベクトルがてどのように演算されるのかは分かった. 今度はSetMatrix()関数によってC#じゃらCgにどのように行列が受け渡されるのかを確認する. というのもUnityのC#は列オーダーでシェーダは行オーダーだという記述が見られて自分の頭は大混乱だから. シェーダで行列の値を直接簡単に確認する方法が無いので,C#内での行列matをCg内の行列_matにSetMatrix()関数で受け渡し, 既知のベクトルvecを関数mul(_mat, vec)で掛け算した結果をフラグメントシェーダの出力を色で確認する.


実験

具体的にはこのプログラム.Vector4()で初期化しているのは列ベクトルであることに注意.

//C#
void Start()
{
    Vector4 col1 = new Vector4(1.0f, 1.0f, 1.0f, 0.0f);
    Vector4 col2 = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
    Vector4 col3 = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
    Vector4 col4 = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
    Matrix4x4 mat = new Matrix4x4(col1, col2, col3, col4);

    this.GetComponent<Renderer>().material.SetMatrix("_mat", mat);
}

つまり送った行列はこれ.

\[ \begin{equation} \left[ \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{array} \right] \end{equation} \]

そんで受け取る行列はこれ.

//Cg
float4x4 _mat;
fixed4 frag (v2f i) : SV_Target
{
    float4 vec = float4(1, 0, 0, 1);
    vec = mul(_mat, vec);

    return vec;
}

float4(1, 0, 0, 1)を列ベクトルだとみなし(UnityのCgシェーダでのベクトルと行列の演算(1)参照),受け取った行列が送った行列そのままだとすると,mul(_mat, vec)が行っている計算はこれになるはず.

\[ \left[ \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{array} \right] \left[ \begin{array}{c} 1 \\ 0 \\ 0 \\ 1 \end{array} \right] = \left[ \begin{array}{c} 1 \\ 1 \\ 1 \\ 1 \end{array} \right] \]

(R,G, B, A)=(1,1,1,1)になるので白が表示されるはず.

一方,受け取った行列_matが送った行列matの転地になっているなら,

\[ \left[ \begin{array}{cccc} 1 & 1 & 1 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{array} \right] \left[ \begin{array}{c} 1 \\ 0 \\ 0 \\ 1 \end{array} \right] = \left[ \begin{array}{c} 1 \\ 0 \\ 0 \\ 1 \end{array} \right] \]

(R,G, B, A)=(1,0,0,1)で赤になるはず.

結果

結果は,編集画面ではアタッチされたオブジェクトは赤で表示され,実行すると白に変わった. ということは,行列は送った状態のまま受け取ったことになる.

Unityの編集面でのカメラのPreviewでは実行する前はシェーダのみが自動的に実行された状態で表示される. このとき_matはC#によって値を渡されていないので単位行列になっている. _matが単位行列だとmul()関数計算後もvecの初期値から変化がないはずなので,(R,G,B,A)=(1,0,0,1)で赤になる.