pre or post multiplication for matrices?

Started by
6 comments, last by PolyVox 18 years, 10 months ago
Right, I seem to have gotten myself a little confused. It stems from the fact that I always thought matrix multiplication for transformations was done using *pre*multiplying. So that: [new_object_pos] = [trans] * [old_object_pos] where all 3 variables are 4x4 matrices. However now I'm not so sure, I think i might have got confused because when multiplying a *vector* by a matrix you have to use premultiplication (or the dimensions don't match). I also looked at the OpenGL spec and it clearly states that glMultMatrix() performs *post*multiplication on the currently selected matrix. So, to clarify, if I have an object represented by some vertices in local space and a matrix giving it's position/orientation, do I move it by using pre or post multiplication? And is there any use for whichever one is not used in this case? Thanks!
Advertisement
There is a simple rule:
Always multiply a line by a column.

So if you have a matrix A and a vector v, you can calculate the transformed v' by:
v' = A*v
Take the first line of A and multiply it with the first column of v (there is just one), and you get the element of v' in the first line and first column. And so on.

If you transpose your equation (mirror on the diagonal), you get:
v't = vt*At

The pre-multiplication becomes a post-multiplication.
You have this cases:

trasformed vertex = vertex x Transformation Matrix

trasformed vertices (Matrix with 1 vertex per row) = vertices (1 vertex x row) x Transformation Matrix

so you use always post-multiplication.

P.S.- Remember that the product is always a row x a column.

Hope that helps.

[Edit: nmi has been faster than me [grin]]
Fil (il genio)
You are correct that the inner dimensions must match in order to multiply two matrices. The vector and matrix obviously differ in one dimension, so they must be arranged a certain way in order to multiply them. However, there are two ways to accomplish this:

[m11 m12][x]
[m21 m22][y]

Or:

[x y][m11 m12]
-----[m21 m22]

(The dashes are just for formatting.)

In the first example the vector is a column vector, in the second a row vector.

Column vectors:

1. Used by OpenGL
2. Matrix applied to left of vector
3. Concatenated transformations are applied from right to left

Row vectors:

1. Used by D3D
2. Matrix applied to right of vector
3. Concatenated transformations are applied from left to right

As far as I know, the academic community prefers column vectors, and most references assume that convention.

One practical implication for OpenGL users is that operations are applied from right to left, in the reverse order of multiplication. That is why when writing OpenGL code, you apply the transform that you want to occur first last, and vice versa.

Some more info. Whether you use row or column vectors, it is sometimes convenient mathematically to transpose, in which case a row vector becomes a column vector and vice versa. For example, a row vector on the left times a column vector on the right produces a 1x1 matrix, i.e. a scalar, and is equivalent to the dot product. A column vector on the left times a row vector on the right produces an NxN matrix, and is the tensor product.

Furthermore, multiplying a row vector with a column-vector matrix or vice versa is equivalent to multiplying the original vector by the transpose of the matrix. With rotation matrices, this can be used to effect 'forward' and 'backward' transforms. I don't know if this is 'legitimate' mathematically or not, but I've seen it done in practice.
Thanks guys, that answers some questions but I'm more concerned about multiplying 4x4 matrices by 4x4 matrices, rather than matrices by vectors. So if I have an object whose position/orientation/scale is represented by a single 4x4 matrix do I pre or post multiply it by the transformation matrix?

Or maybe it's like you said with vectors, in that it varies between OpenGL and Direct3D? And I can still switch between the two by using the transpose?
The primary difference between D3D and OpenGL is notation.

OpenGL: v' = Mv
D3D: v' = vM

also the following are equivalent:

OpenGL: v' = ABCv
DX: v' = vCBA

Although the layout of the elements of matrices and vectors are written differently on paper, they are at the same locations in memory. Now, you could use tranpose to switch between OpenGL and DX, but you don't need to. Transpose works because AB=(BTAT)T, but you just remember that the locations in memory are the same but the notation is different, you can avoid a lot of unnecessary transpositions.

Finally, I'm not 100% sure of this but as I understand it, "pre" and "post" don't refer specifically to which side multiplication symbol the matrix is on, they depend on the notation. For example, in both expressions A is post-concatenated and C is pre-concatenated.

OpenGL: v' = A*B*C*v = A*(B*(C*v)))
DX: v' = v*C*B*A = (((v*C)*B)*A


Quote:Original post by esuvs
Thanks guys, that answers some questions but I'm more concerned about multiplying 4x4 matrices by 4x4 matrices, rather than matrices by vectors. So if I have an object whose position/orientation/scale is represented by a single 4x4 matrix do I pre or post multiply it by the transformation matrix?

Or maybe it's like you said with vectors, in that it varies between OpenGL and Direct3D? And I can still switch between the two by using the transpose?


First, think what you want these matrices do.
Let we have transformation matrices A and B . We want to transform vector V by both and get vector V'.
But above is not enough to specify what we want to do, as
first transform by A then transform by B
and
first transform by B then transform by A
are different. You can just try to play with rotations in real world.(get a cube, paint x,y,z on sides, etc.)

Now, more on how it's done:

first transform by A then transform by B
It is easy to see that this is accomplished by:
In openGL's convention:
V'=B*(A*V)
Note that it is same as
V'=(B*A)*V
so you can do it as
C=B*A;
V'=C*V;
In DX's convention:
V'=(V*A)*B
C=A*B;
V'=V*C;
(note: real operations done on memory is the same. It's just matter how we map matrix indices to memory)
As you see, V*A*B and B*A*V is kind of symmetrical. Same for consequently applied transforms like V*A*B*C*D*E*F or F*E*D*C*B*A*V

Now, write it down for "transform by B then transform by A" yourself to remember it better.

When you render something , you want points to be transformed by
matrix of object
matrix of parent object
matrix of grandparent object ...
matrix of camera
(and done by API, projection matrix)
From there you can find what order of multiplications you want. In OGL, you need to
m=matrix_of_camera*matrix_of_grandparent_objectmatrix_of_parent object*matrix_of_object;
...
then if you'll glLoadMatrix, OpenGL will do
vertice=m*vertice;

Some more notes: Let you have spacecraft with spacecraft-to-world matrix M
And you use OpenGL convention.
For example, to turn spacecraft around spacecraft's local X direction, you need to
M=M*axis_angle_to_matrix(Vector(1,0,0),angle);
(note: you need to reorthonormalize matrix if you do that incrementally)
To turn spacecraft around global X direction, you need to
M=axis_angle_to_matrix(Vector(1,0,0),angle)*M;


On openGL:

This sequence

glMatrixMode(GL_MODELVIEW);
glMultMatrix(A);
glMultMatrix(B);
glMultMatrix(C);
... render your mesh ...
works as if your mesh would be transformed by C,then B, then A
It makes lot of sense, if you for example want to render a robot with matrices giving positions of child nodes (fingers) relativelty to parent (arm), and arm are responsible for positioning fingers, you can do
RenderArm(){
glPushMatrix();
glMultMatrix(matrix_of_first_finger);
RenderFinger();
glPopMatrix();
glPushMatrix();
glMultMatrix(matrix_of_second_finger);
RenderFinger();
glPopMatrix();
}
and so-on(simplified example)
Then, you can have
glPushMatrix();
glMultMatrix(matrix_of_arm);
RenderArm();
glPopMatrix();

and it will work properly.

If you have more questions, ask.
Great, you've all been most helpful! Especisally Dmytry for the clear explanation and details. My question is fully answered though i think I shall experiment a little bit. Rate-ups all round i think....

This topic is closed to new replies.

Advertisement