Verwechslung zwischen C ++ - und OpenGL-Matrixreihenfolge (Zeilenmajor vs. Spaltenmajor)


77

Ich bin gründlich verwirrt über Matrixdefinitionen. Ich habe eine Matrixklasse, die eine enthält, von float[16]der ich angenommen habe, dass sie Zeilenmajor ist, basierend auf den folgenden Beobachtungen:

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 } };

matrixAund matrixBbeide haben das gleiche lineare Layout im Speicher (dh alle Zahlen sind in Ordnung). Laut http://en.wikipedia.org/wiki/Row-major_order weist dies auf ein Zeilen-Haupt-Layout hin.

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

Daher matrixB[0]= Zeile 0, matrixB[1]= Zeile 1 usw. Dies zeigt wiederum das Zeilen-Haupt-Layout an.

Mein Problem / meine Verwirrung entsteht, wenn ich eine Übersetzungsmatrix erstelle, die wie folgt aussieht:

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

Welches in Erinnerung geblieben ist als , { 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }.

Wenn ich dann glUniformMatrix4fv aufrufe , muss ich das Transponierungsflag auf GL_FALSE setzen, um anzuzeigen, dass es spaltenmäßig ist. Andernfalls werden Transformationen wie translate / scale usw. nicht richtig angewendet:

Wenn die Transponierte GL_FALSE ist, wird angenommen, dass jede Matrix in der Hauptreihenfolge der Spalte geliefert wird. Wenn die Transponierte GL_TRUE ist, wird angenommen, dass jede Matrix in der Hauptreihenfolge der Zeile geliefert wird.

Warum muss meine Matrix, die Zeilenmajor zu sein scheint, als Spaltenmajor an OpenGL übergeben werden?


Wie stellen Sie fest, dass Sie "das Transponierungsflag auf setzen müssen GL_FALSE"? Wie benutzt du die Uniform?
Angew ist nicht mehr stolz auf SO

@Angew Ich muss das Transponierungsflag auf GL_FALSE setzen, sonst funktionieren Übersetzungen / Skalen usw. nicht, sie wenden transponierte Transformationen auf die Ansicht an.
Mark Ingram

1
Ich habe hier etwas näher auf das Thema eingegangen. "Matrizen sind keine Transformationen": bit.ly/1cPINCm .
DuckMaestro

Es gibt einen ausgezeichneten Artikel dazu auf Scratchapixel: Scratchapixel.com/lessons/3d-basic-lessons/lesson-4-geometry/… .
user18490

12
Es ärgert mich wirklich, wenn Leute kommen und eine Frage (oder Antwort) abstimmen und kein Feedback hinterlassen ...
Mark Ingram

Antworten:


71

Die in der OpenGL-Dokumentation verwendete Matrixnotation beschreibt nicht das speicherinterne Layout für OpenGL-Matrizen

Wenn Sie denken, dass es einfacher ist, wenn Sie das gesamte "Zeilen- / Spalten-Haupt" -Ding löschen / vergessen. Dies liegt daran, dass der Programmierer neben Zeilen- / Spaltenmajor auch entscheiden kann, wie er die Matrix im Speicher anordnen möchte (ob benachbarte Elemente Zeilen oder Spalten bilden), zusätzlich zur Notation, was zu Verwirrung führt.

OpenGL-Matrizen haben das gleiche Speicherlayout wie DirectX-Matrizen .

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

oder

{ 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 sind 3-Komponenten-Vektoren, die das Matrixkoordinatensystem beschreiben (lokales Koordinatensystem innerhalb relativ zum globalen Koordinatensystem).

  • p ist ein 3-Komponenten-Vektor, der den Ursprung des Matrixkoordinatensystems beschreibt.

Das bedeutet, dass die Übersetzungsmatrix wie folgt im Speicher angelegt werden sollte:

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

Lassen Sie es dabei, und der Rest sollte einfach sein.

--- Zitat aus alten opengl faq--


9.005 Sind OpenGL-Matrizen Spalten- oder Zeilenmajor?

Für Programmierzwecke sind OpenGL-Matrizen 16-Wert-Arrays mit zusammenhängend im Speicher angeordneten Basisvektoren. Die Übersetzungskomponenten belegen das 13., 14. und 15. Element der 16-Elemente-Matrix, wobei die Indizes von 1 bis 16 nummeriert sind, wie in Abschnitt 2.11.2 der OpenGL 2.1-Spezifikation beschrieben.

Column-Major versus Row-Major ist eine reine Notationskonvention. Beachten Sie, dass das Nachmultiplizieren mit Spaltenhauptmatrizen das gleiche Ergebnis liefert wie das Vormultiplizieren mit Zeilenhauptmatrizen. Die OpenGL-Spezifikation und das OpenGL-Referenzhandbuch verwenden beide die Spalten-Hauptnotation. Sie können eine beliebige Notation verwenden, sofern dies eindeutig angegeben ist.

Leider hat die Verwendung des Spalten-Hauptformats in der Spezifikation und im Blue Book zu endloser Verwirrung in der OpenGL-Programmiergemeinschaft geführt. Die Spalten-Hauptnotation legt nahe, dass Matrizen nicht so im Speicher angeordnet sind, wie es ein Programmierer erwarten würde.



2
Wie meinst du das gleiche Speicherlayout? Anhand des Beispiels in meinem ursprünglichen Beitrag wird angezeigt, dass das Speicherlayout meiner Matrix zeilengroß ist (da C den Speicher in zweidimensionalen Arrays anordnet). Warum muss ich das Flag setzen, um OpenGL mitzuteilen, dass ich Column-Major verwende, wenn das Speicherlayout nicht übereinstimmt?
Mark Ingram

2
@MarkIngram: Zeile / Spalte ist eine Notationssache, die NUR in der Dokumentation verwendet wird. Sowohl in D3DMatrix- als auch in OpenGL-Matrix nehmen Übersetzungselemente dieselbe Position im Speicher ein - Elemente mit Index / Offset 12, 13, 14 (oder 13., 14. und 15. Elementen eines 16-Elemente-Arrays).
SigTerm

2
@MarkIngram: Mit anderen Worten, die in der OpenGL-Dokumentation verwendete Matrixnotation beschreibt nicht das speicherinterne Layout von OpenGL-Matrizen.
SigTerm

8
In Column- und Row-Major geht es darum, wie die Matrix im Speicher angeordnet ist. Sehen. z.B. das Buch Golub - Matrix Computations oder der Wiki-Artikel über Row-Major. Die OpenGL-FAQ sind falsch oder mehrdeutig. Es ist eine separate Frage, ob Sie Vektoren als Zeilen- oder Spaltenvektoren behandeln, die auch bestimmt, ob Sie Ihre Basisvektoren in Spalten oder Zeilen behalten sollen. (Manchmal wird der Begriff Zeilenmajor verwendet, um sich darauf zu beziehen, was zur Verwirrung beiträgt.) Der Matrixdatentyp von GLSL ist Spaltenmajor, und die Konvention besteht darin, Vektoren als Spaltenvektoren zu behandeln. Im Speicher sind die D3D- und OpenGL-Konventionen identisch.
Darklon

5
Könnten Sie bitte eine Erklärung hinzufügen, was Ihrer Meinung nach Spalten- / Zeilenmajor tatsächlich bedeutet? Ich verstehe die Bemerkung "Vergiss ..." überhaupt nicht. Laut Wikipedia definieren diese Begriffe das Speicherlayout, was hier der wichtigste Teil ist. Warum sollte ich das vergessen wollen?
Andreas Haferburg

66

Um die Antworten von SigTerm und dsharlet zusammenzufassen: Die übliche Methode zum Transformieren eines Vektors in GLSL besteht darin, den Vektor mit der Transformationsmatrix nach links zu multiplizieren:

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

Damit dies funktioniert, erwartet OpenGL, dass das Tvon SigTerm beschriebene Speicherlayout wie folgt lautet:

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

was auch als "Column Major" bezeichnet wird. In Ihrem Shader-Code (wie in Ihren Kommentaren angegeben) haben Sie den Vektor jedoch mit der Transformationsmatrix rechts multipliziert:

v_transformed = v*T;

was nur dann das richtige Ergebnis liefert, wenn Tes transponiert wird, dh das Layout hat

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

(dh "Row Major"). Da Sie Ihrem Shader bereits das richtige Layout zur Verfügung gestellt haben, nämlich Row Major, war es nicht erforderlich, das transposeFlag von zu setzen glUniform4v.


7
beste Antwort meiner Meinung nach. Es ist ein perfektes Beispiel für Grafikstudenten. In der Mathematik ist einer der Vektoren horizontal, während der andere vertikal ist (andernfalls ist das Produkt undefiniert). Ich weiß, dass wir beim Programmieren die meisten mathematischen Formalismen vergessen. XD
CoffeDeveloper

Warum nicht auch diesen Formalismus vergessen? Aus programmtechnischer Sicht wäre dies viel sinnvoller.
Whossname

15

Sie haben es mit zwei getrennten Problemen zu tun.

Zunächst befassen sich Ihre Beispiele mit dem Speicherlayout. Ihr [4] [4] -Array ist Zeilenmajor, da Sie die von C-mehrdimensionalen Arrays festgelegte Konvention verwendet haben, um mit Ihrem linearen Array übereinzustimmen.

Die zweite Ausgabe ist eine Frage der Konvention, wie Sie Matrizen in Ihrem Programm interpretieren. Mit glUniformMatrix4fv wird ein Shader-Parameter festgelegt. Ob Ihre Transformation für einen Zeilenvektor oder eine Spaltenvektortransformation berechnet wird , hängt davon ab, wie Sie die Matrix in Ihrem Shader-Code verwenden. Da Sie sagen, dass Sie Spaltenvektoren verwenden müssen, gehe ich davon aus, dass Ihr Shader-Code die Matrix A und einen Spaltenvektor x verwendet , um x ' = A x zu berechnen .

Ich würde argumentieren, dass die Dokumentation von glUniformMatrix verwirrend ist. Die Beschreibung des Transponierungsparameters ist ein Umweg, um nur zu sagen, dass die Matrix transponiert ist oder nicht. OpenGL selbst transportiert diese Daten nur zu Ihrem Shader. Ob Sie sie transponieren möchten oder nicht, ist eine Konventionssache, die Sie für Ihr Programm festlegen sollten.

Dieser Link hat einige gute weitere Diskussion: http://steve.hollasch.net/cgindex/math/matrix/column-vec.html


Ich mache nichts Besonderes mit Matrizen in meinem Shader. Es sieht so aus:gl_Position = vec4(v_xy, 0.0, 1.0) * v_ViewMatrix * v_ProjectionMatrix;
Mark Ingram

@ MarkIngram, das vielleicht nicht schick aussieht, aber wichtig ist. Das sagt Ihnen, was Ihre Konvention für die Interpretation Ihrer Transformationsmatrizen ist.
Dsharlet

1
Was sagt es mir (dh was ist die Konvention und wie zeigt es mir)? :)
Mark Ingram

1
@MarkIngram zeigt an, dass Sie Ihre Positionen als Zeilenvektoren interpretieren. Wenn Sie einen Vektor x durch eine Matrix A transformieren möchten, berechnen Sie x '^ T = x ^ T A. Dies ist nicht der Standard in der allgemeinen Mathematik, aber in der Computergrafik üblich (leider führt dies zu viel Verwirrung).
Dsharlet

2
Also sollte ich die Reihenfolge der Multiplikation ändern? Anstatt zu vector x model x view x projection;tun projection x view x model x vector;?
Mark Ingram
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.