CGソフトや書籍ごとの行列(matrix)の違い|行優先と列優先

Tech

はじめに

私が初めて行列を学んだとき、本に書いてある行列演算をCGソフトで実践しようとしたら、思い通りの結果にならず混乱しました。

原因は、CGソフトや書籍によって同じ変換でも行列の書き方が異なることでした。
読み替えが必要だったのです。

例えば、座標(3,4,5)の点をY軸方向に6移動する変換は、以下のようになります。

MayaやHoudini

[3451][1000010000100601]\begin{bmatrix} 3 &4 &5 &1 \end{bmatrix} \begin{bmatrix} 1 &0 &0 &0\\[8pt] 0 &1 &0 &0\\[8pt] 0 &0 &1 &0\\[8pt] 0 &6 &0 &1 \end{bmatrix}

BlenderやBifrost

[1000010600100001][3451]\begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &6\\ 0 &0 &1 &0\\ 0 &0 &0 &1 \end{bmatrix} \begin{bmatrix} 3\\ 4\\ 5\\ 1 \end{bmatrix}

また、以下の画像はMayaとBifrostでそれぞれ同じ変換を表していますが、数字の配置が異なっていることが分かります。

この数字配置の違いは、行優先で表しているか列優先で表しているかによるものです。

これらは「式全体を転置する」というルールで相互に変換できます。

この記事では、行優先/列優先の見分け方・各ソフトの分類・読み替えの具体的手順をまとめます。

「行優先」「列優先」という言葉は、データ保持形式の違いを指す場合と数学的表現の違いを指す場合があります。
この記事では基本的に数学的表現について扱います。

行優先(row-major)と列優先(column-major)

ソフトや書籍によって「行優先」か「列優先」かが異なっており、初心者の混乱の原因となっています。

ベクトル

ベクトルは、行優先では行ベクトル、列優先では列ベクトルとして表されます。

:[a1a2a3]:[a1a2a3]行優先: \begin{bmatrix} a_1 &a_2 &a_3 \end{bmatrix} \qquad 列優先: \begin{bmatrix} a_1\\ a_2\\ a_3 \end{bmatrix}

行列

行優先では、1・2・3行目がX軸・Y軸・Z軸を表し、4行目が移動を表します。列優先では、1・2・3列目がX軸・Y軸・Z軸を表し、4列目が移動を表します。

:[XxXyXz0YxYyYz0ZxZyZz0TxTyTz1]:[XxYxZxTxXyYyZyTyXzYzZzTz0001]行優先:\begin{bmatrix} X_x &X_y &X_z &0\\ Y_x &Y_y &Y_z &0\\ Z_x &Z_y &Z_z &0\\ T_x &T_y &T_z &1 \end{bmatrix} \qquad 列優先: \begin{bmatrix} X_x &Y_x &Z_x &T_x\\ X_y &Y_y &Z_y &T_y\\ X_z &Y_z &Z_z &T_z\\ 0 & 0& 0&1 \end{bmatrix}

合成順

合成変換では、変換行列を適用順に並べます。

行優先ではベクトルの右側(後ろ側)にM1,M2,M3M_1, M_2, M_3…と並べます。
V=VM1M2M3V’ = VM_1M_2M_3

一方、列優先では左側(前側)にM3,M2,M1M_3, M_2, M_1と並べます。

V=M3M2M1VV’ =M_3M_2M_1 V

行優先列優先
V=VM1M2M3V’ = VM_1M_2M_3V=M3M2M1VV’ =M_3M_2M_1 V
V’:変換後のベクトル M:変換行列 V:変換前のベクトル

例えば行優先のソフトであるMayaでは、ベクトルが一番上となり、その後ろ(ノードグラフでは下)に順番に行列をかけていきます。

逆にBifrostは列優先なので、ベクトルはポートの一番下となり、下から順に行列演算が適用されます。

MayaとBifrostのノードグラフにおける行列の合成順の比較

読み替え

結論から言うと、行優先(列優先)の資料の計算を列優先(行優先)のソフトで再現する場合は、行列を転置し合成順を逆にして読み替える必要があります。
使用するソフトが行優先か列優先か分かっていれば、簡単に読み替えられます。

転置とは行列の行と列を入れ替えることです。
(i,j)成分が(j,i)成分となります。
肩のTは転置を表します。

:[123456789]T=[147258369]例: \begin{bmatrix} 1 &2 &3\\ 4 &5 &6\\ 7 &8 &9\\ \end{bmatrix}^T = \begin{bmatrix} 1 &4 &7\\ 2 &5 &8\\ 3 &6 &9\\ \end{bmatrix}

以下のように行優先でベクトルに3つの行列を順に掛け合わせる式を考えます。

VM1M2M3VM_1M_2M_3

これを列優先に読み替えるには式全体を転置すればいいので、同じ変換を列優先では以下のように表せます。

(VM1M2M3)T  M3TM2TM1TVT (VM_1M_2M_3)^T または M_3^TM_2^TM_1^TV^T

具体例として『原点に寄せて(Y-5) → 2倍に拡大 → 元に戻す(Y+5)』という合成変換を考えます。

行優先では以下のように表せます。

[1231][1000010000100501][2000020000200001][1000010000100501]\begin{bmatrix} 1 &2 &3 &1 \end{bmatrix} \begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &0\\ 0 &0 &1 &0\\ 0 &-5 &0 &1 \end{bmatrix} \begin{bmatrix} 2 &0 &0 &0\\ 0 &2 &0 &0\\ 0 &0 &2 &0\\ 0 &0 &0 &1 \end{bmatrix} \begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &0\\ 0 &0 &1 &0\\ 0 &5 &0 &1 \end{bmatrix}

これを転置することで列優先に変換できます。

([1231][1000010000100501][2000020000200001][1000010000100501])T=[1000010500100001][2000020000200001][1000010500100001][1231]\begin{align*} & (\begin{bmatrix} 1 &2 &3 &1 \end{bmatrix} \begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &0\\ 0 &0 &1 &0\\ 0 &-5 &0 &1 \end{bmatrix} \begin{bmatrix} 2 &0 &0 &0\\ 0 &2 &0 &0\\ 0 &0 &2 &0\\ 0 &0 &0 &1 \end{bmatrix} \begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &0\\ 0 &0 &1 &0\\ 0 &5 &0 &1 \end{bmatrix})^T \\ &= \begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &5\\ 0 &0 &1 &0\\ 0 &0 &0 &1 \end{bmatrix} \begin{bmatrix} 2 &0 &0 &0\\ 0 &2 &0 &0\\ 0 &0 &2 &0\\ 0 &0 &0 &1 \end{bmatrix} \begin{bmatrix} 1 &0 &0 &0\\ 0 &1 &0 &-5\\ 0 &0 &1 &0\\ 0 &0 &0 &1 \end{bmatrix} \begin{bmatrix} 1\\ 2\\ 3\\ 1 \end{bmatrix} \end{align*}

行優先/列優先のソフトや書籍の例

最後に、行優先のソフト、列優先のソフトの例を挙げます。

行優先(row-major)列優先(column-major)
MayaBifrost
HoudiniBlender
DirectXOpenGL
『3Dグラフィックスのための数学入門』Unity ※シェーダーのメモリレイアウトは行優先
『実例で学ぶゲーム3D数学』『ゲーム開発のための数学・物理学入門』
『プログラミングのための線形代数』
『線形代数入門』(斎藤正彦)

まとめ

  • ソフトや書籍によって、行列演算の数式の見た目が異なる。
  • 行優先と列優先を読み替えるには、式全体を転置すればよい。
  • 移動値の位置(4行目 or 4列目)やベクトルの向き(行 or 列)で判別できる。

コメント

タイトルとURLをコピーしました