GLSL 4×4 Matrix Fields




GLSL 4x4 transformation matrix

Here is a short article about manipulating GLSL mat4 fields. In C, an OpenGL 4×4 matrix is a 16-float array:

float c_matrix[16];

In GLSL the same matrix is:

mat4 glsl_matrix;

Let’s see how to map the C matrix to the GLSL one and vice-versa. Matrices in OpenGL are column-major. The c_matrix[] can be represented by:


OpenGL 4x4 column-major matrix

The first column is made up of entries 0, 1, 2 and 3. The second column is 4, 5, 6, 7 and so on.

In C, the first entry of the first column is:

c_matrix[0];

While the second entry of the third column is:

c_matrix[9];

Now in GLSL. The first entry of the first column is:

glsl_matrix[0][0];

The second entry of the third column is:

glsl_matrix[2][1];

A mat4 can be seen as four vec4 vectors:

vec4 c0 = glsl_matrix[0].xyzw;
vec4 c1 = glsl_matrix[1].xyzw;
vec4 c2 = glsl_matrix[2].xyzw;
vec4 c3 = glsl_matrix[3].xyzw;


OpenGL 4x4 column-major matrix

The first entry of the first column is now:

glsl_matrix[0].x;

And the second entry of the third column is:

glsl_matrix[2].y;

In the case of an object transformation matrix, the 4×4 matrix usually contains the orientation and the position of the object. The orientation is stored in the upper 3×3 matrix, or in terms of a mat4, in the three first entries of the three first columns. The position is stored in the three first entries of the fourth and last column.

Here is now a GLSL code snippet that build a 4×4 transformation matrix from a 3D position and the three Euler’s angles:

mat4 build_transform(vec3 pos, vec3 ang) 
{
  float cosX = cos(ang.x);
  float sinX = sin(ang.x);
  float cosY = cos(ang.y);
  float sinY = sin(ang.y);
  float cosZ = cos(ang.z);
  float sinZ = sin(ang.z);

  mat4 m;

  float m00 = cosY * cosZ + sinX * sinY * sinZ; 
  float m01 = cosY * sinZ - sinX * sinY * cosZ; 
  float m02 = cosX * sinY;
  float m03 = 0.0;
  
  float m04 = -cosX * sinZ; 
  float m05 = cosX * cosZ; 
  float m06 = sinX;
  float m07 = 0.0;
  
  float m08 = sinX * cosY * sinZ - sinY * cosZ;
  float m09 = -sinY * sinZ - sinX * cosY * cosZ;
  float m10 = cosX * cosY;
  float m11 = 0.0;
  
  float m12 = pos.x;
  float m13 = pos.y;
  float m14 = pos.z;
  float m15 = 1.0;

  /*
  //------ Orientation ---------------------------------
  m[0] = vec4(m00, m01, m02, m03); // first column.
  m[1] = vec4(m04, m05, m06, m07); // second column.
  m[2] = vec4(m08, m09, m10, m11); // third column.

  //------ Position ------------------------------------
  m[3] = vec4(m12, m13, m14, m15); // fourth column.
  */

  //------ Orientation ---------------------------------
  m[0][0] = m00; // first entry of the first column.
  m[0][1] = m01; // second entry of the first column.
  m[0][2] = m02;
  m[0][3] = m03;
  
  m[1][0] = m04; // first entry of the second column.
  m[1][1] = m05; // second entry of the second column.
  m[1][2] = m06;
  m[1][3] = m07;

  m[2][0] = m08; // first entry of the third column.
  m[2][1] = m09; // second entry of the third column.
  m[2][2] = m10;
  m[2][3] = m11;
  
  //------ Position ------------------------------------
  m[3][0] = m12; // first entry of the fourth column.
  m[3][1] = m13; // second entry of the fourth column.
  m[3][2] = m14;
  m[3][3] = m15;

  return m;
}

Remark: manipulating mat4 fields can be dangerous. For example this code does not generate errors nor warnings (at least with my GTX 660 on Windows) but does not work at all:

  //------ Position ------------------------------------
  m[3][0] = m12; // first entry of the fourth column.
  m[3][1] = m13; // second entry of the fourth column.
  m[3][4] = m14; // ERROR!
  m[3][3] = m15;

In the third line, we set m14 to an out of range memory location. Such kind of bugs can be hard to detect…

If you wish to quick test mat4 fields, I prepared a little GLSL Hacker demo you can find in the code sample pack (host_api/GLSL_Misc/mat4_transform/demo.xml).


GLSL Hacker - 4x4 transformation matrix demo

Some references



3 comments

  • Damian Trebilco

    I will have to double check but I believe your memory layout ordering is wrong.

    OpenGL is column major, but is laid out in memory in column order.

    Eg. vec4 columns[4]; // First column is the first 4 memory slots

    A little known fact is that the memory layout of OpenGL matrices is the same as DirectX!
    (eg glTranslatef and D3DXMatrixTranslation should return the same in memory results)
    http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html

  • slimy

    openGL is actually row major. People are confused by it because when they are looking at the modelview matrix they are looking at model * view and don’t consider that view is a transposed matrix.

    Strictly model matrices (ie what you would pass to glMultMatrix) are row major. The first 4 values are the right vector. The next 4 the up vector, the next 4 the forward vector and the last 4 translation.

  • oldcrow

    This whole row-major vs column-major thing is really confusing because it depends how you look at your matrices; some books place the homogeneous components in the last column and use pre-multiplication in order to transform vectors, while other books place the homogeneous components in the last row of the matrix and use post-multiplication. Depending on how you are accustomed to writing your matrices, you may think that memory layout in OpenGL is row-major or column-major. The easiest way to avoid confusion is to drop the whole “row- column-major” thing and just remember that OpenGL places the translation components of matrices at the 13th, 14th, and 15th elements of a 16-element array. And as Damian Trebilco pointed out, both Direct3D and OpenGL use the same matrix layout.