1

行列を使用した 3D グラフィックス用の 2 つの異なる線形数学ライブラリを比較しています。2 つのライブラリからの 2 つの類似した変換関数を次に示します。

static Matrix4<T> Translate(T x, T y, T z)
{
    Matrix4 m;
    m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
    m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
    m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
    m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1;
    return m;
}

(SO ユーザーのプライドアウトからの c++ ライブラリ)

static inline void mat4x4_translate(mat4x4 T, float x, float y, float z)
{    
mat4x4_identity(T);
T[3][0] = x;
T[3][1] = y;
T[3][2] = z;
 }

(SO ユーザー datenwolf の linmath c ライブラリ)

私はこれに慣れていませんが、行列の乗算の順序は、列優先または行優先の形式を使用しているかどうかに大きく依存することを知っています。

私の目には、これら 2 つは同じ形式を使用しており、最初のインデックスは行として扱われ、2 番目のインデックスは列として扱われます。つまり、どちらもx y z同じ最初のインデックスに適用されます。これは行優先であることを意味するため、行列の乗算は連想のままになります (たとえば、通常はrotate * translateその順序で a を実行します)。

左連想コンテキストで最初の例を何度も使用しましたが、期待どおりに機能しています。私は2番目を使用していませんが、著者はそれが右結合であると言いますが、2つの形式の違いを見るのに苦労しています。

4

2 に答える 2

6

私の目には、これら 2 つは同じ形式を使用しており、両方とも最初のインデックスが行として扱われ、2 番目のインデックスが列として扱われます。

見た目は間違っているかもしれませんが、実際には linmath.h の最初のインデックスは列です。C および C++ では、このように定義された多次元配列で

sometype a[n][m];

n x m個の何らかのタイプの要素が連続しています。それが行または列の主要な順序である場合、インデックスをどのように解釈するかによってのみ異なります。現在、OpenGL は、次の線形スキームでインデックス付けされる 4×4 行列を定義しています。

0 4 8 c
1 5 9 d
2 6 a e
3 7 b f

C++ 多次元配列の規則を適用する場合は、次の列行指定を追加します

   ----> n

|  0 4 8 c
|  1 5 9 d
V  2 6 a e
m  3 7 b f

線形インデックスを次の 2 タプルに再マップします。

0 -> 0,0
1 -> 0,1
2 -> 0,2
3 -> 0,3
4 -> 1,0
5 -> 1,1
6 -> 1,2
7 -> 1,3
8 -> 2,0
9 -> 2,1
a -> 2,2
b -> 2,3
c -> 3,0
d -> 3,1
e -> 3,2
f -> 3,3

わかりました、OpenGL と一部の数学ライブラリは、列優先順を使用しています。しかし、なぜこのようにして、M i,jのインデックスiが行を指定し、jが列を指定するという通常の数学的な規則を破るのでしょうか? それは物事をより良く見せるからです。ご覧のとおり、行列は単なるベクトルの集まりです。ベクトルは、座標ベース システムを形成することができ、通常は形成します。

この写真を見てください:

右手ベースのベクトルを持つ 3 次元デカルト座標系

軸 X、Y、Z は基本的にベクトルです。それらは次のように定義されます

X = (1,0,0)
Y = (0,1,0)
Z = (0,0,1)

ちょっと、あそこは恒等行列のように見えませんか? 確かにそうですし、実際にそうです

ただし、このままでは行列は行ベクトルを積み上げて形成されています。また、行列乗算のルールは、基本的に、行ベクトルで形成された行列は、左結合乗算によって行ベクトルを行ベクトルに変換することを示しています。列主行列は、右結合乗算によって列ベクトルを列ベクトルに変換します。

左連想は右連想と同じことを行うことができるので、これは実際には問題ではありません。行と列をすべて交換 (つまり転置) し、オペランドの順序を逆にするだけです。ただし、左<>右行<>列は、私たちが物事を書くための単なる表記規則です。

そして典型的な数学的表記法は(例えば)

v_clip = P · V · M · v_local

この表記により、何が起こっているのかが直感的にわかります。さらに、プログラミングでは、キー文字=は通常、右から左への割り当てを指定します。一部のプログラミング言語は、Pascal や Delphi など、より数学的な影響を受けて記述し:=ます。とにかく、行優先順では、それを書く必要があります

v_clip = v_local · M · V · P

大多数の数学者にとって、これは不自然に見えます。技術的には、M、V、および P は実際には線形演算子であり (はい、行列と線形変換でもあります)、演算子は常に等値/代入と変数の間を行き来するためです。

そのため、列優先形式を使用しています。より見栄えがよくなります。技術的には、行優先形式を使用して行うこともできます。そして、これは行列のメモリ レイアウトとどのような関係があるのでしょうか? 列優先順表記を使用する場合は、要素ごとに抽出することなく、変換行列の基底ベクトルに直接アクセスする必要があります。数値を列優先形式で格納すると、行列の特定の基底ベクトルにアクセスするために必要なのは、線形メモリ内の単純なオフセットだけです。

他のライブラリのコード例について話すことはできませんが、最初のインデックスをゆっくりとインクリメントするインデックスとしても扱うため、OpenGL の表記法に従うと列優先で機能すると強く思います。覚えておいてください: 列優先 & 右結合 == 行優先 & 左結合。

于 2013-03-23T11:25:51.233 に答える
2

投稿されたフラグメントは、質問に答えるのに十分ではありません。これらは、行順に格納された行優先の行列、または列順に格納された列優先の行列である可能性があります。

適切な行列を乗算したときにベクトルがどのように扱われるかを見ると、より明白になる場合があります。行優先のシステムでは、ベクトルが単一の行行列として扱われることが予想されますが、列優先のシステムでは、同様に単一の列の行列になります。次に、ベクトルと行列を乗算する方法を指定します。右側の 1 列または左側の 1 行としてのみ、ベクトルと行列を乗算できます。

GL 規則は列優先であるため、ベクトルは右に乗算されます。D3D は行優先であるため、ベクトルは行であり、左に乗算されます。

変換を連結するときは、正しい順序で適用されるように、これを考慮する必要があります。

すなわち:

GL:
    V' = CAMERA * WORLD * LOCAL * V
D3D:
    V' = V * LOCAL * WORLD * CAMERA

ただし、メモリ内表現が実際には同じになるように行列を格納することを選択します (シェーダーに入り、いくつかの表現を転置する必要があるまで...)

于 2013-03-23T11:16:41.397 に答える