Matrix math problem

Started by
5 comments, last by haegarr 12 years, 8 months ago
This will probably sound silly and sorry if this is in the wrong catagory, but I've recently decided to try and write my own vector and matrix classes to hopefully later be used in a 3d engine... but even though I seem to have no problems writing games were 3d vectors are manipulated by functions, I don't think I really know what I'm doing with Matrix transformations...

I think I've written my Matrix library correctly so far (but currently only written multiply and inverse functions), but I don't think I'm getting the order in which I multi[ly them working correctly.

I know that MatrixA * MatrixB != MatrixB * MatrixA and that MatrixA * (-MatrixA) = -MatrixA * MatrixA = IdentityMatrix... But I think the order in which I'm preforming the multiplication is incorrect when trying to rotate and transform an axis matrix,,, Or it could even be that I am incorrectly feeding the axis information in wrong... I really don't know.

In short, are there any tutorials online that might help me relearn the correct order to set up this kind of thing?

Here's an example of trying to rotate an axis around a point that is not the centre of the axis which does not work... Not sure why.


zAxis = vec3d(0,0,1,0); //<---create Z axis
yAxis = vec3d(0,1,.5,0); //<----fake Y axis

xAxis = zAxis.cross(yAxis); //<----create X axis from cross product of axis Z & Y
yAxis = xAxis.cross(zAxis); //<-----correct Y axis with cross product of axis X & Z

xAxis = xAxis.normalize(); // normalize vectors
yAxis = yAxis.normalize();
zAxis = zAxis.normalize();
axis = MATRIX (xAxis.getX(),xAxis.getY(),xAxis.getZ(),0,
yAxis.getX(),yAxis.getY(),yAxis.getZ(),0,
zAxis.getX(),zAxis.getY(),zAxis.getZ(),0,
0,0,0,1); // <-----Set up axis matrix from 3 axis vectors

axis*= -MATRIX (1,0,0,0,
0,1,0,0,
0,0,1,0,
5,7,0,1); // <---Multiply axis by inverse translation matrix

axis = axis*MATRIX (cos(cy*.05),sin(cy*.05),0,0,
-sin(cy*.05),cos(cy*.05),0,0,
0,0,1,0,
0,0,0,1); //<----multiply axis by Z rotation Matrix

axis = axis*MATRIX (1,0,0,0,
0,1,0,0,
0,0,1,0,
5,7,0,1); //<----Multiply axis by tranlation Matrix to undo inverted translation Matrix

xAxis = vec3d(axis.get(0,0),axis.get(1,0),axis.get(2,0),0);
yAxis = vec3d(axis.get(0,1),axis.get(1,1),axis.get(2,1),0);
zAxis = vec3d(axis.get(0,2),axis.get(1,2),axis.get(2,2),0); //<---just used to display the axis lines

vertToArray(); //<----applies axis Matrix multiply to all verts in model using newVert=vert*Matrix for each.
Advertisement
I hope this will help you: http://www.euclideanspace.com/maths/
Anton

I hope this will help you: http://www.euclideanspace.com/maths/


Thanks for that, it looks like a good primer and hopefully it will help me address the order problem.
I'll probably try to the first few chapters into a pdf and have them for reference for now.
--Where are the text mark-up options gone to? --


Don't confuse inversion with negation. Inversion is meant w.r.t. an operation, and its outcome is also dependent on the operation. E.g. negation is the inversion for addition, so that
f + -f == 0
while the reziprocal is the inversion of the multiplication, so that
f * f^-1 == 1
(I'm not a mathematician but I hope you understand what I mean.)

For matrices both kinds exists, and "inversion" is usually meant in the context of multiplication:
M * M^-1 == I


The order of multiplication of matrices depends on whether you use column or row matrices, because of
( M1 * M2 )^t == M2^t * M1^t
where t denotes the transpose operator. As you can see, the order reverses if you choose "the other" convention.

Besides this, the relative order of transformations depends on the desires of the user. In general one wants to transform by using the local axes. This implies the common order
S * R * T
(when using row vectors), where S means scaling, R menas rotation, and T means translation.

Often R is composed using Euler angles. There is a couple of rotations known as Euler angles. A typical usage is pitching, then heading, then banking. Because you want to do so around local axes, the formula using row vectors would be (I neglect the details here)
R := B * H * P

Sometimes you want to use arbitrary origins or axes. In such cases you need to multiply on both sides of the original transformation matrix (I neglect the details here once again). Eg. to use an arbitrary center of rotation, you need to use
C^-1 * R * C

In summary: A library must provide both multiplication on the left and on the right side. And it has to be absolutely clear whether column or row vectors are in use!
Some more things:

1. It is Good Practice to make non-member functions for operations that are not directly related to a class. E.g.
float dot( vec2f const&, vec2f const&) instead of float vec2f::dot( vec2f const& ) const
vec3f cross( vec3f const&, vec3f const& ) instead of vec3f vec3f::cross( vec3f const& ) const

2. The cross product is not commutative. The rule is
v1 x v2 = -( v2 x v1 )
Now, with the usual order X Y Z of axes, this means that
X x Y = Z
Y x Z = X
Z x X = Y
but especially
Z x Y = -X
X x Z = -Y
Please check your code snippet w.r.t. this issue.
@Haegarr:
Thanks for that... seems to make sense to me mostly... but I just got up and some of it confused me... hopefully after class I can have another look at it and it will make even more sense.
I thought that in 4x4 matrices a tranlation was possible through multiplication... but in truth I haven't even got an negation function.... Nor had I thought about them....

I think I have even more research to do.

Here are the methods for multiplication and inversion I wrote (just in case there are obviouse errors)...
The multiplication function is just a standard row dot column loop,
the inversion is using Gauss-Jordan Elimination (well; my understanding of it).
Neither are written for speed... just trying to put together a working class.


MATRIX MATRIX::operator*(const MATRIX &o) const
{
float tarr[4][4];
int tA,tB;
for (tA = 0; tA < 4; tA++)
{
for (tB = 0; tB < 4; tB++)
{
tarr[tB][tA]=vec3d(m[0][tB],m[1][tB],m[2][tB],m[3][tB]).dot(vec3d(o.m[tA][0],o.m[tA][1],o.m[tA][2],o.m[tA][3]));
}
}

return MATRIX(tarr[0][0],tarr[1][0],tarr[2][0],tarr[3][0],
tarr[0][1],tarr[1][1],tarr[2][1],tarr[3][1],
tarr[0][2],tarr[1][2],tarr[2][2],tarr[3][2],
tarr[0][3],tarr[1][3],tarr[2][3],tarr[3][3]);
}


MATRIX MATRIX::operator-() const
{
float Spare[8][4];
int tA,tB,tC,tD;
float fC;

//left
Spare[0][0] =m[0][0];
Spare[1][0] =m[1][0];
Spare[2][0] =m[2][0];
Spare[3][0] =m[3][0];

Spare[0][1] =m[0][1];
Spare[1][1] =m[1][1];
Spare[2][1] =m[2][1];
Spare[3][1] =m[3][1];

Spare[0][2] =m[0][2];
Spare[1][2] =m[1][2];
Spare[2][2] =m[2][2];
Spare[3][2] =m[3][2];

Spare[0][3] =m[0][3];
Spare[1][3] =m[1][3];
Spare[2][3] =m[2][3];
Spare[3][3] =m[3][3];

//right
Spare[4][0] =1;
Spare[5][0] =0;
Spare[6][0] =0;
Spare[7][0] =0;

Spare[4][1] =0;
Spare[5][1] =1;
Spare[6][1] =0;
Spare[7][1] =0;

Spare[4][2] =0;
Spare[5][2] =0;
Spare[6][2] =1;
Spare[7][2] =0;

Spare[4][3] =0;
Spare[5][3] =0;
Spare[6][3] =0;
Spare[7][3] =1;


char tmp[64];

for (tA = 0;tA<4;tA++)
{
if (Spare[tA][tA]!=0)
{
fC=Spare[tA][tA];
for (tB=0;tB<8;tB++)
{
if (Spare[tB][tA]!=0) Spare[tB][tA]=Spare[tB][tA]/fC;
}
for (tB=0;tB<4;tB++)
{
if (tB!=tA)
{
fC=Spare[tA][tB];
for (tD=0;tD<8;tD++)
{
Spare[tD][tB]-=(fC*Spare[tD][tA]);
//Spare[tD+4][tB]-=fC*Spare[tD+4][tA];
}
}
}
} else {
Log("!!!!ERROR!!!!!!!");
}

}

return MATRIX (Spare[4][0], Spare[5][0], Spare[6][0], Spare[7][0],
Spare[4][1], Spare[5][1], Spare[6][1], Spare[7][1],
Spare[4][2], Spare[5][2], Spare[6][2], Spare[7][2],
Spare[4][3], Spare[5][3], Spare[6][3], Spare[7][3]
);
}

I thought that in 4x4 matrices a tranlation was possible through multiplication... but in truth I haven't even got an negation function.... Nor had I thought about them....

Well, things are more complicated. Assume a 2D space in which to do transformations. Assume that you have a 2x2 matrix R for rotation and a 2 vector t for translation. Using row vectors, a transformed point P' then is computed from its original point P as
P' := P * R + t
Writing this as set of scalar equations gives
P'[sub]x[/sub] := P[sub]x[/sub] * R[sub]xx[/sub] + P[sub]y[/sub] * R[sub]yx[/sub] + t[sub]x[/sub]
P'[sub]y[/sub] := P[sub]x[/sub] * R[sub]xy[/sub] + P[sub]y[/sub] * R[sub]yy[/sub] + t[sub]y[/sub]
[size="1"]
If we want to express the translation within the matrix product, the t[sub]x[/sub] and t[sub]y[/sub] must become part of R, but then we need to multiply it with another component of the point P. This is because In a matrix product the count of columns in the left matrix (the vector P in this case) and the count of rows in the right matrix (R in this case) must be equal. This additional component need to be 1, because e.g. t[sub]x[/sub] * 1 == t[sub]x[/sub] doesn't change anything:
P'[sub]x[/sub] := P[sub]x[/sub] * R[sub]xx[/sub] + P[sub]y[/sub] * R[sub]yx[/sub] + t[sub]x [/sub]* 1
P'[sub]y[/sub] := P[sub]x[/sub] * R[sub]xy[/sub] + P[sub]y[/sub] * R[sub]yy[/sub] + t[sub]y[/sub] * 1

But now R is no longer square and P' has 2 components but P has 3. To overcome this and to allow the resulting P' to be used in a further transformation, the same extension under consideration of the mechanics of the matrix product shows us that
P'[sub]x[/sub] := P[sub]x[/sub] * R[sub]xx[/sub] + P[sub]y[/sub] * R[sub]yx[/sub] + t[sub]x [/sub]* 1
P'[sub]y[/sub] := P[sub]x[/sub] * R[sub]xy[/sub] + P[sub]y[/sub] * R[sub]yy[/sub] + t[sub]y[/sub] * 1
P'[sub]w[/sub] := 1 = P[sub]x[/sub] * 0 + P[sub]y[/sub] * 0 + 1 * 1
will do the job. Hence, using such an extension into a 3rd dimension allows us to write
P'[sub]h[/sub] := P[sub]h[/sub] * M[sub]h[/sub] = P[sub]h[/sub] * R[sub]h[/sub] * T[sub]h[/sub]
[sub][/sub]
This additional co-ordinate is named the homogeneous co-ordinate, and vectors or matrices using this are named homogeneous vectors or matrices, resp.

So: It is not only an additional dimension that allows to incorporate a translation as matrix product. It is further a special use of this co-ordinate. You'll see during your journey that there are more things with this homogeneous stuff like differing between point and direction vectors, normalized and unnormalized points, and its use for perspective projection.


Here are the methods for multiplication and inversion I wrote (just in case there are obviouse errors)...
The multiplication function is just a standard row dot column loop,
the inversion is using Gauss-Jordan Elimination (well; my understanding of it).
Neither are written for speed... just trying to put together a working class.

It is just so that the unary minus is commonly used for negation, and I've interpreted its occurrence just that way. Using it as matrix inversion is confusing most people, and that is a Bad Thing for a library.

This topic is closed to new replies.

Advertisement