87 votes

Confusion entre l'ordre des matrices C++ et OpenGL (row-major vs column-major)

Je m'embrouille complètement dans les définitions des matrices. J'ai une classe de matrice, qui contient une float[16] que j'ai supposé être row-major, sur la base des observations suivantes :

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

matrixA y matrixB les deux ont la même disposition linéaire en mémoire (c'est-à-dire que tous les chiffres sont dans l'ordre). D'après http://en.wikipedia.org/wiki/Row-major_order cela indique une mise en page de type "row-major".

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

Par conséquent, matrixB[0] = rang 0, matrixB[1] = rang 1, etc. Là encore, il s'agit d'une disposition en rangées majeures.

Mon problème / ma confusion survient lorsque je crée une matrice de traduction qui ressemble à ceci :

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

Qui se présente sous la forme d'une mémoire, { 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 } .

Alors quand j'appelle glUniformMatrix4fv Je dois définir l'indicateur de transposition sur GL_FALSE, indiquant qu'il s'agit d'une colonne majeure, sinon les transformations telles que translate / scale etc. ne seront pas appliquées correctement :

Si la transposition est GL_FALSE, chaque matrice est supposée être fournie en ordre majeur des colonnes. Si la transposition est GL_TRUE, chaque matrice est supposée être est supposée être fournie dans l'ordre principal des lignes.

Pourquoi ma matrice, qui semble être à majorité de lignes, doit-elle être transmise à OpenGL à majorité de colonnes ?

77voto

Roberto Points 664

Pour résumer les réponses de SigTerm et dsharlet : La façon habituelle de transformer un vecteur en GLSL est de multiplier à droite la matrice de transformation par le vecteur :

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

Afin que cela fonctionne, OpenGL s'attend à ce que la disposition de la mémoire de T à être, tel que décrit par SigTerm,

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

qui est également appelée "colonne majeure". Dans votre code de shader (comme indiqué par vos commentaires), cependant, vous avez multiplié à gauche la matrice de transformation par le vecteur :

v_transformed = v*T;

qui ne donne le bon résultat que si T est transposée, c'est-à-dire qu'elle a la disposition

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

(c'est-à-dire "row major"). Puisque vous avez déjà fourni la disposition correcte à votre shader, à savoir la rangée principale, il n'était pas nécessaire de définir l'attribut transpose drapeau de glUniform4v .

76voto

SigTerm Points 16055

La notation matricielle utilisée dans la documentation d'Opengl ne décrit pas la disposition en mémoire des matrices OpenGL.

Je pense que ce sera plus facile si vous laissez tomber/oubliez tout le truc "ligne/colonne-majeur". En effet, en plus de la majeure ligne/colonne, le programmeur peut également décider de la manière dont il souhaite disposer la matrice dans la mémoire (si les éléments adjacents forment des lignes ou des colonnes), en plus de la notation, ce qui ajoute à la confusion.

Les matrices OpenGL ont même disposition de la mémoire que les matrices 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

ou

{ 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 sont des vecteurs à 3 composantes décrivant le système de coordonnées de la matrice (système de coordonnées local par rapport au système de coordonnées global).

  • p est un vecteur à 3 composantes décrivant l'origine du système de coordonnées de la matrice.

Ce qui signifie que la matrice de traduction doit être disposée en mémoire comme ceci :

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

Restez-en là, et le reste devrait être facile.

---citation de l'ancienne faq d'opengl--


9.005 Les matrices OpenGL sont-elles à dominante colonne ou à dominante ligne ?

Pour des raisons de programmation, les matrices OpenGL sont des tableaux de 16 valeurs avec des vecteurs de base disposés de manière contiguë dans la mémoire. Les composantes de translation occupent les 13e, 14e et 15e éléments de la matrice à 16 éléments, où les indices sont numérotés de 1 à 16 comme décrit dans la section 2.11.2 de la spécification OpenGL 2.1.

La différence entre colonne-major et ligne-major est une pure convention de notation. Notez que la post-multiplication avec des matrices de type colonne-major produit le même résultat que la pré-multiplication avec des matrices de type ligne-major. La spécification OpenGL et le manuel de référence OpenGL utilisent tous deux la notation column-major. Vous pouvez utiliser n'importe quelle notation, tant que c'est clairement indiqué.

Malheureusement, l'utilisation du format column-major dans la spécification et le livre bleu a entraîné une confusion sans fin dans la communauté de programmation OpenGL. La notation colonne-major suggère que les matrices ne sont pas disposées en mémoire comme un programmeur s'y attendrait.


Je vais mettre à jour cette réponse vieille de 9 ans.

Une matrice mathématique est définie comme suit m x n matrice. Où m est un nombre de rangées y n est le nombre de colonnes . Par souci d'exhaustivité, les lignes sont horizontales et les colonnes sont verticales. Lorsque l'on désigne un élément de matrice en notation mathématique Mij le premier élément ( i ) est un index de ligne, le second ( j ) est un indice de colonne. Lorsque deux matrices sont multipliées, à savoir A(m x n) * B(m1 x n1) la matrice résultante a le nombre de lignes du premier argument ( A ), et le nombre de colonnes de la seconde( B ), et le nombre de colonnes du premier argument ( A ) doit correspondre au nombre de lignes du second ( B ). donc n == m1 . C'est clair jusqu'ici, oui ?

Maintenant, en ce qui concerne la disposition en mémoire. Vous pouvez stocker la matrice de deux façons. Row-major et column-major. Row-major signifie qu'effectivement vous avez des lignes disposées l'une après l'autre, linéairement. Ainsi, les éléments vont de gauche à droite, ligne après ligne. Un peu comme du texte anglais. Column-major signifie qu'en fait, vous avez colonnes disposés l'un après l'autre, linéairement. Les éléments commencent donc en haut à gauche, et vont de haut en bas.

Exemple :

//matrix
|a11 a12 a13|
|a21 a22 a23|
|a31 a32 a33|

//row-major
[a11 a12 a13 a21 a22 a23 a31 a32 a33]

 //column-major
[a11 a21 a31 a12 a22 a32 a13 a23 a33]

Maintenant, voici la partie amusante !

Il existe deux façons de stocker une transformation 3D dans une matrice. Comme je l'ai déjà mentionné, une matrice en 3D stocke essentiellement les vecteurs de base du système de coordonnées et la position. Vous pouvez donc stocker ces vecteurs dans les lignes ou les colonnes d'une matrice. Lorsqu'ils sont stockés en colonnes, vous multipliez une matrice avec un vecteur colonne. Comme ceci.

//convention #1
|vx.x vy.x vz.x pos.x|   |p.x|   |res.x|
|vx.y vy.y vz.y pos.y|   |p.y|   |res.y|
|vx.z vy.z vz.z pos.z| x |p.z| = |res.z|
|   0    0    0     1|   |  1|   |res.w| 

Cependant, vous pouvez également stocker ces vecteurs sous forme de lignes, et vous multiplierez alors un vecteur de ligne avec une matrice :

//convention #2 (uncommon)
                  | vx.x  vx.y  vx.z 0|   
                  | vy.x  vy.y  vy.z 0|   
|p.x p.y p.z 1| x | vz.x  vz.y  vz.z 0| = |res.x res.y res.z res.w|
                  |pos.x pos.y pos.z 1|   

Donc. La convention n° 1 apparaît souvent dans les textes mathématiques. La convention n°2 est apparue dans le sdk de DirectX à un moment donné. Les deux sont valables.

Et en ce qui concerne la question, si vous utilisez la convention n°1, alors vos matrices sont en colonne majeure. Et si vous utilisez la convention n°2, alors elles sont en ligne. Cependant, la disposition de la mémoire est la même dans les deux cas.

[vx.x vx.y vx.z 0 vy.x vy.y vy.z 0 vz.x vz.y vz.z 0 pos.x pos.y pos.z 1]

C'est pourquoi j'ai dit qu'il était plus facile de mémoriser quel élément est quel autre, il y a 9 ans.

16voto

dsharlet Points 616

Vous traitez de deux questions distinctes.

Tout d'abord, vos exemples traitent de la disposition de la mémoire. Votre tableau [4][4] est une ligne majeure parce que vous avez utilisé la convention établie par les tableaux multidimensionnels du C pour correspondre à votre tableau linéaire.

La deuxième question est une question de convention sur la façon dont vous interprétez les matrices dans votre programme. glUniformMatrix4fv est utilisé pour définir un paramètre de shader. Que votre transformation soit calculée pour un vecteur de rangée ou vecteur colonne La transformation est une question de la façon dont vous utilisez la matrice dans votre code de shader. Puisque vous dites que vous devez utiliser des vecteurs colonnes, je suppose que votre code de shaders utilise la matrice. A et un vecteur colonne x pour calculer x' \= A x .

Je dirais que la documentation de glUniformMatrix est déroutant. La description du paramètre de transposition est une façon très détournée de dire que la matrice est transposée ou ne l'est pas. OpenGL lui-même ne fait que transporter ces données vers votre shader, que vous vouliez les transposer ou non est une question de convention que vous devez établir pour votre programme.

Ce lien permet d'approfondir la discussion : http://steve.hollasch.net/cgindex/math/matrix/column-vec.html

2voto

bikeman868 Points 591

Je pense que les réponses existantes ici sont très peu utiles, et je peux voir dans les commentaires que les gens se sentent confus après les avoir lues, donc voici une autre façon d'envisager la situation.

En tant que programmeur, si je veux stocker un tableau en mémoire, je ne peux pas stocker une grille rectangulaire de chiffres, car la mémoire de l'ordinateur ne fonctionne pas comme ça, je dois stocker les chiffres dans une séquence linéaire.

Disons que j'ai une matrice 2x2 et que je l'initialise dans mon code comme ceci :

const matrix = [a, b, c, d];

Je peux utiliser cette matrice avec succès dans d'autres parties de mon code, à condition de savoir ce que représente chacun des éléments du tableau.

La spécification OpenGL définit ce que chaque position d'index représente, et c'est tout ce que vous avez besoin de savoir pour construire un tableau et le passer à OpenGL pour qu'il fasse ce que vous attendez.

La question de la ligne ou de la colonne principale n'entre en jeu que lorsque je veux écrire ma matrice dans un document qui décrit mon code, car les mathématiciens écrivent les matrices comme des grilles rectangulaires de nombres. Cependant, il ne s'agit que d'une convention, d'une façon d'écrire les choses, et cela n'a aucun impact sur le code que j'écris ou sur la disposition des nombres dans la mémoire de mon ordinateur. Vous pourriez facilement réécrire ces documents mathématiques en utilisant une autre notation, et cela fonctionnerait tout aussi bien.

Pour le tableau ci-dessus, j'ai deux options pour écrire ce tableau dans ma documentation sous forme de grille rectangulaire :

|a b|  OR  |a c|
|c d|      |b d|

Quelle que soit la façon dont je choisis d'écrire ma documentation, cela n'aura aucun impact sur mon code ou sur l'ordre des chiffres dans la mémoire de mon ordinateur, c'est juste de la documentation.

Pour que les personnes qui lisent ma documentation sachent dans quel ordre j'ai stocké les valeurs dans le tableau linéaire de mon programme, je peux spécifier qu'il s'agit d'une représentation du tableau sous forme de matrice par colonne ou par ligne. S'il s'agit d'un ordre majeur en colonnes, je dois parcourir les colonnes pour obtenir l'arrangement linéaire des nombres. S'il s'agit d'une représentation par rangées, je dois parcourir les rangées pour obtenir la disposition linéaire des nombres.

En général, écrire la documentation dans l'ordre majeur des lignes facilite la vie des programmeurs, car si je veux traduire cette matrice

|a b c|
|d e f|
|g h i|

en code, je peux l'écrire comme ceci :

const matrix = [
  a, b, c
  d, e, f
  g, h, i
];

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X