web-dev-qa-db-ja.com

C ++とOpenGLのマトリックスの順序の混乱(行優先と列優先)

マトリックスの定義について完全に混乱しています。次の観察に基づいて、行クラスであると想定したfloat[16]を保持するマトリックスクラスがあります。

float matrixA[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
float matrixB[4][4] = { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 }, { 12, 13, 14, 15 } };

matrixAmatrixBはどちらもメモリ内で同じ線形レイアウトを持っています(つまり、すべての数字は順番に並んでいます)。 http://en.wikipedia.org/wiki/Row-major_order によると、これは行優先レイアウトを示します。

matrixA[0] == matrixB[0][0];
matrixA[3] == matrixB[0][3];
matrixA[4] == matrixB[1][0];
matrixA[7] == matrixB[1][3];

したがって、matrixB[0] =行0、matrixB[1] =行1などです。これも、行優先レイアウトを示しています。

私の問題/混乱は、次のような翻訳マトリックスを作成するときに発生します。

1, 0, 0, transX
0, 1, 0, transY
0, 0, 1, transZ
0, 0, 0, 1

{ 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }としてメモリに配置されます。

それから glUniformMatrix4fv を呼び出すとき、トランスポーズフラグをGL_FALSEに設定する必要があります。

転置がGL_FALSEの場合、各行列は列のメジャー順に提供されると想定されます。転置がGL_TRUEの場合、各行列は行優先順で提供されると想定されます。

行優先と思われるマトリックスを列優先としてOpenGLに渡す必要があるのはなぜですか?

68
Mark Ingram

OpenGLドキュメントで使用されるマトリックス表記では、OpenGLマトリックスのメモリ内レイアウトが説明されていません

「行/列の主要な」もの全体をドロップ/忘れた方が簡単だと思うなら。これは、プログラマーが行/列メジャーに加えて、混乱に追加する表記に加えて、メモリ内のマトリックスのレイアウト方法(隣接する要素が行または列を形成するかどうか)も決定できるためです。

OpenGL行列には、 directx行列と同じメモリレイアウト があります。

x.x x.y x.z 0
y.x y.y y.z 0
z.x z.y z.z 0
p.x p.y p.z 1

または

{ x.x x.y x.z 0 y.x y.y y.z 0 z.x z.y z.z 0 p.x p.y p.z 1 }
  • x、y、zは、マトリックス座標系(グローバル座標系を基準としたローカル座標系)を記述する3コンポーネントベクトルです。

  • pは、マトリックス座標系の原点を記述する3コンポーネントベクトルです。

つまり、翻訳マトリックスは次のようにメモリに配置する必要があります。

{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }.

そのままにしておけば、あとは簡単です。

---古いopenglのFAQからの引用-


9.005 OpenGL行列は列優先ですか、行優先ですか?

プログラミングの目的では、OpenGL行列は、メモリ内に連続して配置されたベースベクトルを持つ16値配列です。変換コンポーネントは、16要素マトリックスの13番目、14番目、および15番目の要素を占有します。OpenGL2.1仕様のセクション2.11.2で説明されているように、インデックスには1〜16の番号が付けられます。

列メジャーと行メジャーは、純粋に表記上の規則です。列優先行列で事後乗算すると、行優先行列で事前乗算するのと同じ結果になることに注意してください。 OpenGL仕様とOpenGLリファレンスマニュアルは、どちらも列優先表記法を使用しています。明確に記述されている限り、任意の表記法を使用できます。

残念なことに、仕様書とブルーブックで列優先形式を使用すると、OpenGLプログラミングコミュニティで無限の混乱が生じました。列優先表記法は、プログラマーが期待するように、行列がメモリに配置されないことを示唆しています。


64
SigTerm

SigTermとdsharletによる回答を要約すると、GLSLでベクトルを変換する通常の方法は、ベクトルに変換行列を左掛けすることです。

mat4 T; vec4 v; vec4 v_transformed; 
v_transformed = T*v;

それが機能するために、OpenGLはTのメモリレイアウトがSigTermによって記述されているように、

{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }

「列メジャー」とも呼ばれます。ただし、(コメントで示されている)シェーダーコードでは、ベクトルに変換行列を右乗算しました。

v_transformed = v*T;

Tが転置された場合、つまりレイアウトがある場合にのみ正しい結果が得られます

{ 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }

(つまり、「行メジャー」)。シェーダーに正しいレイアウト、つまり行メジャーを既に提供しているため、glUniform4vtransposeフラグを設定する必要はありませんでした。

52
Roberto

2つの別々の問題に対処しています。

まず、あなたの例はメモリレイアウトを扱っています。 [4] [4]配列は、Cの多次元配列で確立された規則を使用して線形配列と一致するため、行優先です。

2番目の問題は、プログラムで行列をどのように解釈するかに関する慣習の問題です。 glUniformMatrix4fvは、シェーダーパラメーターの設定に使用されます。 行ベクトルまたは列ベクトル変換に対して変換が計算されるかどうかはシェーダーコードでマトリックスを使用する方法の問題。列ベクトルを使用する必要があると言うので、シェーダーコードはマトリックス[〜#〜] a [〜#〜]とa計算する列ベクトルxx '=A x

glUniformMatrixのドキュメントは紛らわしいと主張します。転置パラメータの説明は、マトリックスが転置されているか、されていないかを示す、まさに遠回りの方法です。 OpenGL自体は、そのデータをシェーダーに転送するだけです。データを転置するかどうかは、プログラムで確立する必要がある慣習の問題です。

このリンクには、さらに詳しい説明があります。 http://steve.hollasch.net/cgindex/math/matrix/column-vec.html

14
dsharlet