The OpenGL maths library(GLM) uses the following algorithm to compute the translation matrix:
//taken from source code
template
OpenGL Mathematics (GLM) is based on the OpenGL Shading Language (GLSL). What glm::translate actually does is to set up a translation matrix and multiply the input matrix by the translation. It computes m*t
in the meaning of GLSL Vector and Matrix Operations:
mat<4, 4, T, Q> Result(m); Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
(In the following Result
is substituted by R
)
Note, m[0] * v[0]
multiplies each component of the column m[0]
by the scalar v[0]
. The result is the vector (m[0][0]*v[0], m[0][1]*v[0], m[0][2]*v[0], m[0][3]*v[0])
.
So R[3] = m[0]*v[0] + m[1]*v[1] + m[2]*v[2] + m[3]
is the same as
R[3][0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0]
R[3][1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1]
R[3][2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2]
R[3][3] = m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3]
glm::translate
actually calculates:
vh = (v[0], v[1], v[2], 1)
R = m
R[3][0] = dot( (m[0][0], m[1][0], m[2][0], m[3][0]), vh )
R[3][1] = dot( (m[0][1], m[1][1], m[2][1], m[3][1]), vh )
R[3][2] = dot( (m[0][2], m[1][2], m[2][2], m[3][2]), vh )
R[3][3] = dot( (m[0][3], m[1][3], m[2][3], m[3][3]), vh )
The code above computes the Dot product of the rows from m
, by vh
. vh
is the 4th column of the translation t
. Note the translation matrix t
is defined as:
c0 c1 c2 c3
---------------------
r0: 1 0 0 v[0]
r1: 0 1 0 v[1]
r2: 0 0 0 v[2]
r3: 0 0 0 1
A concatenation of 4x4 matrices (R = m*t
) is the Dot product of the rows of m
and the columns of t
and can be expressed as:
(See OpenGL Shading Language 4.60 Specification - 5.10. Vector and Matrix Operations)
for i from 0 to 3
for j fro 0 to 3
R[i][j] = dot( (m[0][j], m[1][j], m[2][j], m[3][j]), t[i] )
Where dot(a, b) == a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]
,
(m[0][j], m[1][j], m[2][j], m[3][j])
is the j-th row of m
and
t[i]
is i-th column of t
.
For glm::translate
it is sufficient to copy R[0]
, R[1]
and R[2]
from m[0]
, m[1]
and m[2]
.
e.g. for (i=0
, j=0
):
R[0][0] = dot( (m[0][0], m[1][0], m[2][0], m[3][0]), t[0] )
R[0][0] = dot( (m[0][0], m[1][0], m[2][0], m[3][0]), (1, 0, 0, 0) )
R[0][0] = m[0][0] * 1 + m[1][0] * 0 + m[2][0] * 0 + m[3][0]) * 0
R[0][0] = m[0][0]
GLM matrices (as OpenGL matrices) are stored in column major order. If you investigate matrices in the debugger that may lead to confusions.
If you have the matrix
c0 c1 c2 c3
-------------------
r0: Xx Yx Zx Tx
r1: Xy Yy Zy Ty
r2: Xz Yz Zz Tz
r3: 0 0 0 1
then the memory image of a 4*4 OpenGL matrix looks like this:
Xx, Xy, Xz, 0, Yx, Yy, Yz, 0, Zx, Zy, Zz, 0, Tx, Ty, Tz, 1
If you investigate it in a debugger, it may look like:
[ [ Xx, Xy, Xz, 0 ],
[ Yx, Yy, Yz, 0 ],
[ Zx, Zy, Zz, 0 ],
[ Tx, Ty, Tz, 1 ] ]
The technical details of as to how the math is done is magnificiently done in @Rabbid76's answer, but if anyone would like to understand why m*t
is computed instead of t*m
then here's the answer:
Computing the matrix tm
like this:
here, you're taking the standard basis as the basis vectors for linear combination, so, essentially you're transforming in world space coordinates. but
doing it the other way around and computing mt
means now you're essentially taking the basis as the m[0]
, m[1]
and m[2]
respectively, so you're transforming in the local space given by the basis, and since this is essentially a model matrix, we just call it model space.
That is probably one way to view it if you're only considering translation, but what if you're handling composite transformations like below:
M=glm::translate(M,T);
R=glm::rotate(M,angle,Rot_axis);
Here the model matrix is M
(initialized to identity at first), T
is the translation matrix, R
the rotation matrix and others are straightforward above.
So the transformation sequence that happens in the above code is:
M.T.R
and say this is applied to the vector v=[x, y, z, 1]
, the vector undergoes first a rotation, then a translation and then only the model transformation is done, if it helps, you may see it like this:
M.(T.R.v)