PDA

View Full Version : Matrix composition



Lucretia
04-17-2004, 04:41 PM
Hi,

I've just started getting back into OpenGL and have started to write some matrix functions. I decided to separate out the 4x4 matrix into a 3x3 matrix for rotation and scale and use a 3 vector for the translation. Now, I worked out the proof that I needed to compose this into a 4x4 matrix.

Given that a 4x4 matrix is composed thus:


|M|T|
M1 = -----
|0|1|Where M1 is a 3x3 matrix and T is a column vector representing the translation.

This is the formula for multiplying 2 4x4 matrices:


|m0 m4 m8 m12| |t0 t4 t8 t12|
M = |m1 m5 m9 m13| T = |t1 t5 t9 t13|
|m2 m6 m10 m14| |t2 t6 t10 t14|
|m3 m7 m11 m15| |t3 t7 t11 t15|

|(m0t0 + m4t1 + m8t2 + m12t3) (m0t4 + m4t5 + m8t6 + m12t7) (m0t8 + m4t9 + m8t10 + m12t11) (m0t12 + m4t13 + m8t14 + m12t15)|
MT = |(m1t0 + m5t1 + m9t2 + m13t3) (m1t4 + m5t5 + m9t6 + m13t7) (m1t8 + m5t9 + m9t10 + m13t11) (m1t12 + m5t13 + m9t14 + m13t15)|
|(m2t0 + m6t1 + m10t2 + m14t3) (m2t4 + m6t5 + m10t6 + m14t7) (m2t8 + m6t9 + m10t10 + m14t11) (m2t12 + m6t13 + m10t14 + m14t15)|
|(m3t0 + m7t1 + m11t2 + m15t3) (m3t4 + m7t5 + m11t6 + m15t7) (m3t8 + m7t9 + m11t10 + m15t11) (m3t12 + m7t13 + m11t14 + m15t15)|So, substituting:


Proof 1:

|m0 m4 m8 0| |1 0 0 t0|
|m1 m5 m9 0| * |0 1 0 t1| =
|m2 m6 m10 0| |0 0 1 t2|
|0 0 0 1| |0 0 0 1 |

|(m0(1) + m4(0) + m8(0) + (0)(0)) (m0(0) + m4(1) + m8(0) + (0)(0)) (m0(0) + m4(0) + m8(1) + (0)(0)) (m0t0 + m4t1 + m8t2 + (0)(0))|
|(m1(1) + m5(0) + m9(0) + (0)(0)) (m1(0) + m5(1) + m9(0) + (0)(0)) (m1(0) + m5(0) + m9(1) + (0)(0)) (m1t0 + m5t1 + m9t2 + (0)(0))| =
|(m2(1) + m6(0) + m10(0) + (0)(0)) (m2(0) + m6(1) + m10(0) + (0)(0)) (m2(0) + m6(0) + m10(1) + (0)(0)) (m2t0 + m6t1 + m10t2 + (0)(0))|
|((0)(1) + (0)(0) + (0)(0) + (1)(0)) ((0)(0) + (0)(1) + (0)(0) + (1)(0)) ((0)(0) + (0)(0) + (0)(1) + (1)(0)) ((0)t0 + (0)t1 + (0)t2 + (1)(1))|

|(m0) (m4) (m8 ) (m0t0 + m4t1 + m8t2 )|
|(m1) (m5) (m9 ) (m1t0 + m5t1 + m9t2 )|
|(m2) (m6) (m10) (m2t0 + m6t1 + m10t2)|
|(0 ) (0 ) (0 ) (1 )|And, now if I multiply a 3x3 matrix with a 3 vector:


Proof 2:

|m0 m4 m8 | |t0|
|m1 m5 m9 | * |t1| =
|m2 m6 m10| |t2|

|m0t0 + m4t1 + m8t2 |
|m1t0 + m5t1 + m9t2 |
|m2t0 + m6t1 + m10t2|So, proof 2 is the same as the translation part of the matrix in proof 1, hence to compose a 4x4 matrix from a 3x3 and a vector, the final formula is:


|M|MT|
M2 = ------
|0|1 |Now, from my code, this doesn't quite work out. Yet, if I don't multiply the translation by the 3x3 matrix when I compose the 4x4, everything is fine, yet I don't quite understand why this works?

I have seen a post on here that noted that M1 is equal to a rotation followed by a translation, and M2 is the opposite. But, surely if this were correct, M2 would work as expected, yet it doesn't; it's almost as though the triangle I'm rotating is being sheared.

Can somebody shed some light on this problem?

Thanks,
Luke.

plasmonster
04-17-2004, 05:37 PM
Be mindfull of the row, column order you're using. Remember, the reverse order law of transposition says that
A^t B^t = (BA)^t, where ^t is transposition.
My hunch is that you're ending up with the transpose, or reverse transpose, of what you want, hence the shearing.

Lucretia
04-18-2004, 01:27 AM
Originally posted by RadicalHumility:
Be mindfull of the row, column order you're using. Remember, the reverse order law of transposition says that
A^t B^t = (BA)^t, where ^t is transposition.
My hunch is that you're ending up with the transpose, or reverse transpose, of what you want, hence the shearing.Yes, but the numbering I've used in the examples, is the correct order for OpenGL, i.e. m0->m15 is an array like:


GLfloat mtx[16];Luke.

Lucretia
04-18-2004, 01:33 AM
Here's the code (in Ada) that I'm using:


procedure Compose(Result : out Object; Left : in Vector3.Object; Right : in Matrix3x3.Object) is

MT : Vector3.Object;

begin

Matrix3x3.Mul(MT, Right, Left);

Result(0) := Right(0);
Result(1) := Right(1);
Result(2) := Right(2);
Result(3) := 0.0;
Result(4) := Right(3);
Result(5) := Right(4);
Result(6) := Right(5);
Result(7) := 0.0;
Result(8) := Right(6);
Result(9) := Right(7);
Result(10) := Right(8);
Result(11) := 0.0;
Result(12) := MT.X;
Result(13) := MT.Y;
Result(14) := MT.Z;
Result(15) := 1.0;

end Compose;Now, if I don't multiply the matrix and translation together and just pass T insteasd of MT into the translation column, it works fine.

I just don't understand why the code I would've expected to work (going by the proofs), doesn't.

Thanks,
Luke.

plasmonster
04-18-2004, 08:45 AM
Have you tried a reverse transpose? I'm not sure about your code, but I am sure that matrix multiplication with gl works correctly. There is a disturbing trend with some in the graphics community to bend the universe around the gl. Do your math the way its done in nearly every text, then do a transpose before you send it to gl. This might save you alot of headache.

Overmind
04-18-2004, 08:59 AM
I don't think there is something wrong with your calculations. Your Proof 1 shows what happens if you call first glRotate and then glTranslate, meaning you first Translate and then Rotate. Remember if you want to transform objects you have to reverse the operations.

Another way to look at it would be to transform not the objects but the local coordinate frame...

Lucretia
04-18-2004, 02:27 PM
Originally posted by Overmind:
I don't think there is something wrong with your calculations. Your Proof 1 shows what happens if you call first glRotate and then glTranslate, meaning you first Translate and then Rotate.Yes, but this isn't what is happening when I use that version of the Compose function. I would expect to see the object rotate around *me*, current'y I'm seeing nothing at all :(

Luke.

plasmonster
04-18-2004, 02:44 PM
Let's back up a tad and make sure we're on the same page. When you call MultMatrix(), you are concatenating a matrix with the current gl matrix:
M = I,
M = M * YourMatrix,
M = M * YourMatrix2,
...
Or, in general,
M' = I * M1 * M2 * ... * Mn.
When you transforma a point with this matrix, you have:
P' = ( I * M1 * (M2 * ... * (Mn * P))).
As you can see, the transformations will be applied in the reverse order. If you understand this, you should be good to go.

Lucretia
04-18-2004, 03:05 PM
Originally posted by Portal:
Let's back up a tad and make sure we're on the same page. When you call MultMatrix(), you are concatenating a matrix with the current gl matrix:
M = I,
M = M * YourMatrix,
M = M * YourMatrix2,
...
Or, in general,
M' = I * M1 * M2 * ... * Mn.
When you transforma a point with this matrix, you have:
P' = ( I * M1 * (M2 * ... * (Mn * P))).
As you can see, the transformations will be applied in the reverse order. If you understand this, you should be good to go.Yes, I understand all of this, but that isn't the problem.

Here's my Draw function that is called on every frame:


procedure Draw is

RX : Matrix3x3.Object;
RY : Matrix3x3.Object;

begin

MeshData.TM.Translation := (Z => Zoom, others => 0.0);

Matrix3x3.RotateX(RX, Maths.Degrees_To_Radians(XSpeed));
Matrix3x3.RotateY(RY, Maths.Degrees_To_Radians(YSpeed));

Matrix3x3.Mul(MeshData.TM.Matrix, RY, RX);

Renderer.Clear;

Renderer.SetMatrix(MeshData.TM.Matrix, MeshData.TM.Translation);
Renderer.DrawMesh(MeshData);


Renderer.SwapBuffers;

end Draw;Now the SetMatrix call does the following:


procedure SetMatrix(Matrix : in Matrix3x3.Object; Translation : in Vector3.Object := (others => 0.0)) is

TM : Matrix4x4.Object;

begin

Matrix4x4.Compose(TM, Translation, Matrix);
GL.glLoadMatrixf(Float_To_GLfloat(TM(TM'First)'Unc hecked_Access));


end SetMatrix;Where that Compose function is the one I posted earlier.

Something is going wrong when I use this Compose function. When I use the oher Compose function which doesn't multipy the matrix3x3 and the Vector3 I can see the object and rotate it in the manner I expect. I expect from what I've read about this Compose is that the operations should occur in the opposite order so I should have the object rotating about me, but that isn't happening. If I remove the translation completely, I can see the object but it is distorted.

Luke.

plasmonster
04-18-2004, 03:17 PM
Remember when you load the matrix to do a transpose, assuming your matrices are row major.
glLoadMatrixf( MyRowMajorMatrix.transpose() ), or something to that effect.

edit:
By the way, the Compose() function looks a little odd, why is the "Left" matrix ignored in the result?

Lucretia
04-19-2004, 12:36 AM
Originally posted by Portal:
Remember when you load the matrix to do a transpose, assuming your matrices are row major.
glLoadMatrixf( MyRowMajorMatrix.transpose() ), or something to that effect.
It's not row major though, it's column major.



edit:
By the way, the Compose() function looks a little odd, why is the "Left" matrix ignored in the result?Left isn't ignored, it's used in the matrix/vector multiply operation to obtain the new translation. Which is exactly the way I calculated it on paper (see the proofs).

Luke.

Overmind
04-22-2004, 05:45 AM
I'm still not sure what the problem is, so I just post here what you should expect with either compose function:

Lets assume R is the rotation matrix and T is the translation matrix.

1) You set the translation vector without multiplying it with the rotation matrix:
( R R R x )
( R R R y )
( R R R z )
( 0 0 0 1 )

This is equal to the matrix multiplication T.R, so it is like you first call translate and then call rotate. In this case I would expect the object to be rotated around its local origin and then the origin is translated to the coordinate (x,y,z), so the position of the object doesn't change with the rotation.

2) You multiply the translation vector with the rotation matrix before the composition (like in the compose function you posted). This is equal to the matrix multiplication R.T, so you call rotate first and then translate. This way the object would be first translated away from you and then it rotates around you (assuming the camera is in the origin...)