matrix layout
3D Matrix Definition
Memory Layout
Matrix Code Definition
C++ class definition
row major implementation
column major implementation
generic definition
- using column or row vectors
- the layout of matrices in memory
3D Matrix Definition
column vectors
In this case the translation will be in elements m14, m24, m34 (marked in bold).
Mathematicians and OpenGL, prefer column vectors (vector on the right)
m11 m12 m13 m14 x m21 m22 m23 m24 y m31 m32 m33 m34 z m41 m42 m43 m44 1row vectors
In this case the translation will be in elements m41, m42 and m43 (marked in bold).
DirectX prefers row vectors (vector on the left)
m11 m12 m13 m14 m21 m22 m23 m24 x y z 1 m31 m32 m33 m34 m41 m42 m43 m44
Matrix Memory Layout
Now if we take the above matrix definition, and put it in a 'flat, contiguous' block of memory of 16 floats - what value should each float represent?
We have two options: we can either go through the rows first, or alternatively we can go through the columns first...
Column Major
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [m11 m21 m31 m41 m12 m22 m32 m42 m13 m23 m33 m43 m14 m24 m34 m44]Row Major
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [m11 m12 m13 m14 m21 m22 m23 m24 m31 m32 m33 m34 m41 m42 m43 m44]Notice that in both cases , the translation is in the elements at index 12, 13 and 14 in both OpenGL and DirectX. These locations map to m41, m42 and m43 in the DirectX convention - and to m14, m24, m34 in the OpenGL convention.
Matrix Code Definition
Now it's important to know where openGL or directX expect the values to be when they are passed a pointer to a matrix - as this determines how we have to define our C++ class.
OpenGL assumes colum major memory layout
DirectX assumes row major memory layout
C++ class definition
Now let's see how we put that into C/C++ sourcecode. Assume we want to use 16 seperate float-values in our matrix class. It could look like this:
class matrix4 { float m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44; }
row major implementation
Now if we assume row-major (directX) a translation matrix would look like this:
m11, m12, m13, m14 1 0 0 0 m21, m22, m23, m24 0 1 0 0 m31, m32, m33, m34 = 0 0 1 0 m41, m42, m43, m44 tx ty tz 1which - using the above class definition - would end up in memory like this:
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] 1 0 0 0 0 1 0 0 0 0 1 0 tx ty tz 1Which is exactly how directX would like to have it - perfect.
column major implementation
Now let's assume a column-major matrix (openGL) a translation matrix would look like this:
m11, m12, m13, m14 1 0 0 tx m21, m22, m23, m24 0 1 0 ty m31, m32, m33, m34 = 0 0 1 tz m41, m42, m43, m44 0 0 0 1which - using the above class definition - would end up in memory like this:
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] 1 0 0 tx 0 1 0 ty 0 0 1 tz 0 0 0 1Which is not how openGL would like to have it. The matrix and the values are ok - but they just end up in the wrong location in memory.
How can we fix this?
What if we would change the order of the member-variables in the class like this:
class matrix4_col { float m11, m21, m31, m41, // 1st column m12, m22, m32, m42, // 2nd column m13, m23, m33, m43, // 3rd column m14, m24, m34, m44; // 4th column };We use the same translation-values, but now they end up in memory like this:
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] 1 0 0 0 0 1 0 0 0 0 1 0 tx ty tz 1which is exactly what openGL would like to have it - perfect.
another example
Now if we see a matrix as 4 seperate vectors (in either row or column order, depending on the graphics API) and we make sure we fill the 4 vectors appropriately ourselves - then we can use the same matrix class-definition for both ( however operations on the values are not necessarily the same!)
class vector4 { float x,y,z,w; } class matrix4 { vector4 x; // either row 1 (directX) or column 1 (openGL) vector4 y; // either row 2 (directX) or column 2 (openGL) vector4 z; // either row 3 (directX) or column 3 (openGL) vector4 w; // either row 4 (directX) or column 4 (openGL) }