今ひとつ完全理解に至っていなかったので自分の言葉で残しておく
間違ってたら妖精さんが直してくれる
(WEB上にたくさんある有用な記事を読んでわかる方は当記事をこれ以上読む必要は無い)
Per-Component 算術演算 - Win32 apps | Microsoft Docs
行列の要素と演算
行列の行と列の意味や行列要素の位置はrow-majorでもcolumn-majorでも変わることはない
行とは行列の横方向のラインであるし、列は縦方向のラインである
mat._11は1行目1列目の要素であり、mat._42は4行目2列目の要素である
行列とベクトルの乗算のルールもrow-majorでもcolumn-majorで変わることはない
(ただしHLSL等ではベクトルは演算の位置によって適宜列ベクトルや行ベクトルに読み替えられる)
V1 = M * V0; // Matrix4x4とVec4
は
V1.x = M._11 * V0.x + M._12 * V0.y + M._13 * V0.y + M._14 * V0.w V1.y = M._21 * V0.x + M._22 * V0.y + M._23 * V0.y + M._24 * V0.w V1.z = M._31 * V0.x + M._32 * V0.y + M._33 * V0.y + M._34 * V0.w V1.w = M._41 * V0.x + M._42 * V0.y + M._43 * V0.y + M._44 * V0.w
であり、
V1 = V0 * M; // Matrix4x4とVec4
は
V1.x = M._11 * V0.x + M._21 * V0.y + M._31 * V0.y + M._41 * V0.w V1.y = M._12 * V0.x + M._22 * V0.y + M._32 * V0.y + M._42 * V0.w V1.z = M._13 * V0.x + M._23 * V0.y + M._33 * V0.y + M._43 * V0.w V1.w = M._14 * V0.x + M._24 * V0.y + M._34 * V0.y + M._44 * V0.w
である
シェーダ側の行列のメモリレイアウト
定数バッファ等でCPUから送られてきた行列のメモリ領域をどのように解釈するか
row-majorとして解釈するのか、column-majorとして解釈するのかを指定する
Matrix4x4を考えた場合、メモリ上では連続する16個の値として扱われる
このメモリをrow-majorな行列として解釈すると
0番目から4つを _11, _12, _13, _14 に読み取り
4番目から4つを _21, _22, _23, _24 に読み取り
8番目から4つを _31, _32, _33, _34 に読み取り
12番目から4つを _41, _42, _43, _44 に読み取る
row-major の場合は先頭から4つずつの行ベクトルとして解釈される
対してこのメモリをcolumn-majorな行列として解釈すると
0番目から4つを _11, _21, _31, _41 に読み取り
4番目から4つを _12, _22, _32, _42 に読み取り
8番目から4つを _13, _23, _33, _43 に読み取り
12番目から4つを _14, _24, _34, _44 に読み取る
column-major の場合は先頭から4つずつの列ベクトルとして解釈される
問題になる場合
CPU側の行列型データのメモリレイアウトと、シェーダ側定数バッファの行列型のメモリレイアウトが一致していない場合に転置された行列として解釈されてしまう
DirectxMathのMatrixはrow-majorな定義となっており、HLSLのデフォルトはcolumn-majorとなっているらしいので注意が必要
行列を左から掛けるのか右から掛けるのか問題
row-majorとcolumn-majorはあくまでメモリレイアウトの問題であり、ベクトルに対して行列を右から掛けるのか左から掛けるのかというのは別問題
例えば回転行列を考えて、
- 基底ベクトルを列ベクトルとして配置した行列
- 左から掛けるべき
- 基底ベクトルを行ベクトルとして配置した行列
- 右から掛けるべき
これはそれぞれの思想に則って決めることだと思う
row-major/colum-majorとは関係なくその行列がどのような情報を表現しているかという問題
(個人的には基底ベクトルは列ベクトルとして配置したい派)