私のプログラムで入力された行列を見ると、4番目、8番目、12番目の要素を占める翻訳コンポーネントが表示されます。
始める前に、理解することが重要です。これは、行列が行優先であることを意味します。したがって、この質問に答えます。
列メジャーWVP行列は、HLSL呼び出しで頂点を変換するために正常に使用されます:mul(vector、matrix)これにより、ベクトルが行優先として扱われるようになります。数学ライブラリによって提供される列メジャー行列はどのように機能しますか?
非常に単純です。行列は行優先です。
多くの人々が行優先または転置行列を使用しているため、行列がそのように自然に方向付けられていないことを忘れています。したがって、彼らは次のように翻訳マトリックスを見る:
1 0 0 0
0 1 0 0
0 0 1 0
x y z 1
これは転置された平行移動行列です。これは、通常の変換行列がどのように見えるかではありません。翻訳は第四に行くの列ではなく、4行目。時々、あなたはこれを完全にゴミである教科書で見ることさえします。
配列内の行列が行優先か列優先かを知るのは簡単です。行優先の場合、翻訳は3、7、11番目のインデックスに格納されます。列優先の場合、翻訳は12、13、および14番目のインデックスに格納されます。もちろんゼロベースのインデックス。
混乱は、実際には行優先の行列を使用しているのに、列優先の行列を使用していると信じていることに起因します。
行メジャーと列メジャーは表記規則であるという記述は完全に真です。行列の乗算と行列/ベクトルの乗算の仕組みは、規則に関係なく同じです。
変化するのは結果の意味です。
結局のところ、4x4マトリックスは、単なる4x4グリッドの数値です。座標系の変更を参照する必要はありません。ただし、特定のマトリックスに意味を割り当てたら、そこに何が格納され、どのように使用するかを知る必要があります。
上記で示した翻訳マトリックスをご覧ください。これは有効な行列です。次float[16]
の2つの方法のいずれかでそのマトリックスを保存できます。
float row_major_t[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1};
float column_major_t[16] = {1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1};
しかし、翻訳が間違った場所にあるため、この翻訳マトリックスは間違っていると言いました。具体的には、次のように変換行列を構築する方法の標準的な規則に関連して転置されると述べました。
1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1
これらがどのように保存されるか見てみましょう:
float row_major[16] = {1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1};
float column_major[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1};
予告column_major
でまったく同じようにrow_major_t
。したがって、適切な変換行列を取得して列優先として保存すると、その行列を転置して行優先として保存するのと同じことになります。
これは、表記規則のみであることを意味します。実際には、メモリの格納と転置という2つの規則があります。メモリストレージは列と行が主ですが、転置は通常と転置です。
行優先の順序で生成された行列がある場合、その行列の列優先の等価物を転置することで同じ効果を得ることができます。およびその逆。
行列の乗算は1つの方法でのみ実行できます。2つの行列が特定の順序で与えられた場合、特定の値を乗算して結果を保存します。現在、A*B != B*A
ですが、の実際のソースコードA*B
はのコードと同じですB*A
。どちらも同じコードを実行して出力を計算します。
行列乗算コードは、行列が列優先順または行優先順のどちらで格納されているかを考慮しません。
同じことは、ベクトル/行列乗算についても言えません。そして、これが理由です。
ベクトル/行列の乗算は誤りです。それはできません。ただし、行列に別の行列を掛けることはできます。したがって、ベクトルが行列であると仮定すると、単純に行列/行列の乗算を実行するだけで、ベクトル/行列の乗算を効率的に実行できます。
4Dベクトルは、列ベクトルまたは行ベクトルと見なすことができます。つまり、4Dベクトルは4x1マトリックス(マトリックス表記では行カウントが最初に来る)または1x4マトリックスと考えることができます。
しかし、これが問題です。2つの行列AとBが与えられたA*B
場合、Aの列数がBの行数と同じである場合にのみ定義されます。したがって、Aが4x4行列の場合、Bは4行の行列でなければなりません。初期化。したがって、A*x
x は行ベクトルです。同様に、x*A
xが列ベクトルの場合は実行できません。
このため、ほとんどの行列数学ライブラリはこの仮定を行っています。ベクトルに行列を掛けると、意味のないものではなく、実際に機能する乗算を実行することになります。
任意の4Dベクトルxについて、以下を定義します。C
列ベクトルでなければならない行列の形x
、及びR
行ベクトルでなければならないマトリックスの形態x
。これを考えると、4x4マトリックスAの場合、A*C
Aに列ベクトルを乗算したマトリックスを表しますx
。またR*A
、行ベクトルx
にAを掛けた行列を表します。
しかし、厳密な行列計算を使用してこれを見ると、これらは同等ではないことがわかります。と同じにR*A
することはできませんA*C
。これは、行ベクトルが列ベクトルと同じではないためです。それらは同じマトリックスではないため、同じ結果は生成されません。
ただし、それらは1つの方法で関連しています。それは事実ですR != C
。ただし、Tが転置演算である場合も同様です。2つの行列は、互いに転置されます。R = CT
ここに面白い事実があります。ベクトルは行列として扱われるので、ベクトルにも列対行主記憶域の問題があります。問題は、どちらも同じように見えることです。floatの配列は同じであるため、データを見ただけではRとCの違いはわかりません。唯一の違いを見分けるための方法は、それらが使用されている方法です。
2つの行列AとBがあり、Aが行優先として、Bが列優先として格納されている場合、それらを乗算してもまったく意味がありません。結果としてナンセンスになります。まあ、そうでもない。数学的には、を実行することと同じです。または; それらは数学的に同一です。AT*B
A*BT
したがって、行列の乗算は、2つの行列(およびベクトル/行列の乗算は行列の乗算のみ)が同じ主な順序で格納されている場合にのみ意味があります。
それで、ベクトルは列優先ですか、行優先ですか?前に述べたように、両方です。列マトリックスとして使用される場合にのみ列メジャーであり、行マトリックスとして使用される場合は行メジャーです。
したがって、列Aである行列Aがある場合、x*A
何も意味しません。まあ、これも意味しますが、それはあなたが本当に望んでいたことではありません。同様に、が行優先の場合、転置乗算を行います。x*AT
A*x
A
したがって、ベクトル/行列乗算の順序はありませんデータのあなたの主要な順序に応じて、変更を(そして、あなたは転置行列を使用しているかどうか)。
次のコードスニペットでr!= r2を実行する理由
コードが壊れていてバグがあるからです。数学的には、。この結果が得られない場合は、等価テストが間違っている(浮動小数点の精度の問題)か、行列乗算コードが壊れています。A * (B * C) == (CT * BT) * AT
なぜpos3!= pos for
それは意味がないからです。真実であるための唯一の方法はifでしょう。そして、それは対称行列にのみ当てはまります。A * t == AT * t
A == AT