Matrix Orientation?

Started by
10 comments, last by Kurioes 19 years, 1 month ago
Hello, I was just wondering how you store the Orientation of an Object in a Matrix? At the moment I am simply using the X, Y and Z rotation Matricies and mulitplying them together and pluging in my angles. Though it doesn't seem to work properly? Is this the right way to go about it, or is there a much better way? Cheers. Mark
Advertisement
I can't say why it's not working, but here's a little info about orientation matrices (just home for lunch, so may be somewhat abreviated).

An orientation matrix stores the basis vectors of the coordinate system it represents in the rows or columns (depending on whether you're using row or column vectors). These basis vectors have to be in reference to something else, so they are usually given in the coordinate frame of the 'parent' coordinate system. For our purposes we'll consider this to be the world coordinate system.

Multiplying a vector by a rotation matrix R takes it from the local space of the rotation matrix to world space. If Pl is the local coordinate, and Pw is the world coordinate, then:

Pw = Pl * R; // Row-major, or...
Pw = R * Pl; // Column-major

Pl = Pw * Rt; // Row-major (t = transpose), or...
Pl = Rt * Pw; // Column-major

Here's another way to look at it. Given basis vectors i and j whose coordinates are expressed in world space, then:

Pw = Pl.x * i + pl.y * j; // And...
Pl = Vector2(Pw.Dot(i), Pw.Dot(j));

If you take the first equations and multiply everything out, you should come out with something equivalent to the bottom equations.

Anyway, gotta go (I hope I got all that right...no time to proof).
Thanks for that, really cleared it all up. What I've been trying to do is draw the orientation. So since my matrix is made up of vector rows I tried drawing each row as an axis.

void cBox::RenderOrr(void){	UpdateOrr();	glColor3f(1.0f, 0.0f, 0.0f);	// Draw X Axis from center of Box	glBegin(GL_LINES);		glVertex3f(Pos.x, Pos.y, Pos.z);		glVertex3f(Pos.x + Orr[0].x * 5.0f, Pos.y + Orr[0].y * 5.0f, Pos.z + Orr[0].z * 5.0f);	glEnd();	// Draw Y Axis from center of Box	glColor3f(0.0f, 1.0f, 0.0f);	glBegin(GL_LINES);		glVertex3f(Pos.x, Pos.y, Pos.z);		glVertex3f(Pos.x + Orr[1].x * 5.0f, Pos.y + Orr[1].y * 5.0f, Pos.z + Orr[1].z * 5.0f);	glEnd();			// Draw Z Axis from center of Box	glColor3f(0.0f, 0.0f, 1.0f);	glBegin(GL_LINES);		glVertex3f(Pos.x, Pos.y, Pos.z);		glVertex3f(Pos.x + Orr[2].x * 5.0f, Pos.y + Orr[2].y * 5.0f, Pos.z + Orr[2].z * 5.0f);	glEnd();}


And I've been updating the Orienatation matrix with this function:

void cBody::UpdateOrr(void){	float xCs = cosf(Ang.x);	float xSn = sinf(Ang.x);	float yCs = cosf(Ang.y);	float ySn = sinf(Ang.y);	float zCs = cosf(Ang.z);	float zSn = sinf(Ang.z);	Orr.R[0] = cVector3(yCs * zCs, yCs * zSn, -ySn);	Orr.R[1] = cVector3(xSn * ySn * zCs - zSn * xCs, xSn * ySn * zSn + xCs * zCs, xSn * yCs);	Orr.R[2] = cVector3(ySn * xCs * zCs + xSn * zSn, ySn * xCs * zSn - xSn * zCs, xCs * yCs);	return;}


Now when it rotates does it rotate around the world axis, or the local orientation axis? And when you rotate around an axis should that axis stay still? At the moment the only axis that stays still all the time while rotating about it is the x axis, initially the y and z do untill the object is rotated around another axis.

Cheers.
Mark
First, the line:

Orr.R[0] = cVector3(yCs * zCs, yCs * zSn, -ySn);

Creates a new vector object and copies it (the compiler may optimize that - I'm not sure). Anyway, perhaps you could instead have a Set() function, like this:

Orr.R[0].Set(yCs * zCs, yCs * zSn, -ySn);

It will probably be more efficient.

Ok, now for the rotation issue. Let's assume your yaw-pitch-roll matrix is correct (I didn't check it, so you might if you're not sure).

The problems you are having are intrinsic to Euler angles. It may not be clear exactly what's happening because you're combining three matrices 'in place' instead of long hand (doing it in place is certainly more efficient).

What's really happening though is that you're taking a matrix which represents an orientation about the world x axis, multiplying it by a world y rotation matrix, and then multiplying that by a world z rotation matrix. But as you noted, only the first axis is actually the world axis. After the first matrix multiplication, the basis vectors have been rotated and the axes no longer correspond with the world axes.

So if your Euler angle order is x, y, z, then you are first rotating about the world x axis, then rotating about the *local* y axis after the x rotation, then rotating around the *local* z axis after the x and y rotations.

(It's been forever since I've used Euler angles - I hope I'm getting this all right.)

Anyway, the first step is probably to make sure your rotations are ordered in a reasonable way. There are different conventions, but it makes a certain amount of sense to do yaw first, then pitch, then roll. What axes these map to depends on your convention, but yaw is around the up axis, pitch around the side axis, and roll around the forward axis.

Since you already have the matrix set up, you might just try changing the sin/cos assignments to change the order of your rotations.

Even if you get that happening, you may still suffer from gimbal lock, which occurs when one axis is rotated into coincidence with another and you lose a degree of freedom. A solution to that is incremental rotations and arbitrary axis/angle matrices, but that's another topic :-)
Thanks again. You say its been ages since you have used Euler angles, I'm just curious what do you use as I think I might look into some different ways of doing this.

Quote:Thanks again. You say its been ages since you have used Euler angles, I'm just curious what do you use as I think I might look into some different ways of doing this.
Yes, you should! I think you'll be happier in the long run.

Before anyone gives further advice, though, it would be helpful to know what sort of camera or player motion you're after. Do you want a standard Quake-style 'move in the xy plane and view with yaw and pitch' camera? Or a Descent-style 'full 6DOF move and look anywhere' camera? The optimal solution will be a little different depending on what you're after.
Its actually not for a camera, it was for projecting objects along a axis so I test for seperation of axis. I was storing my collision boxs as a vector of extents in each direction, and an orientation for which to align the extents. Not sure if this is a good way of doing it or not?

Thanks again for helping out, really appreciate it.
Quote:I was storing my collision boxs as a vector of extents in each direction, and an orientation for which to align the extents. Not sure if this is a good way of doing it or not?
Ah, I see. Yes, that's the standard way.

Here are some ideas about how to handle rotation.

To avoid gimble lock and Euler angles, it's helpful to rotate incrementally. That is, you maintain a single matrix representing your orientation, and then you update that matrix with a rotation *relative* to your current orientation.

Let's just look at yaw, for example, which we'll say is around the y axis. For your object to yaw correctly, you don't want to rotate around the *world* y axis, you want to rotate around the *local* y axis. Recall that the rows or columns of your rotation matrix are the local axes, so the local x, y or z axis can be found easily.

So to yaw your object by angle theta, you want to create a matrix that represents a rotation around your object's local y axis by angle theta, and multiply that by your current matrix. The result is your updated orientation. You can then do the same with pitch and roll. (There are some optimizations that can be made here, but we'll skip those for now.)

For this you need to know how to create an axis-angle matrix. The formulation for this is pretty standard and you should be able to find it online. Just remember that some examples will use column vectors and some will use row vectors, so make sure to check if their convention matches yours; otherwise, transpose the example.

One last thing. After you've rotated your matrix a few times, floating point error will accumulate and it will start to drift out of orthogonality. To prevent this you need to renormalize it occasionally, which just means to correct it so that the axes are again perpendicular to each other. If your axes are x, y and z, here's a bit of code to orthonormalize:

y.Normalize();
x -= x.Dot(y) * y;
x.Normalize();
z = x.Cross(y); // I think that's the right order

There are more sophisticated methods, but that should get the job done.

Once you get all that working, it might be worth looking into quaternions, which do essentially the same thing but have some advantages. But it can certainly all be done with matrices.

Let me know if you have any other questions.
Thanks for that. Sorry for the late reply I've been away from a while. I'm looking into Quaternions. Now I've been trying to generate a quaternion from euler angles. I have the following code:

void cQuaternion::FromAxisAngle(cVector3 Axis, float Theta){	Axis.Normalize();	float Sn = sin(Theta / 2.0f);	float Cs = cos(Theta / 2.0f);	R = Cs;	V.x = Axis.x * Sn;	V.y = Axis.y * Sn;	V.z = Axis.z * Sn;	return;}void cQuaternion::FromEuler(cVector3 A){	cMatrix3 I; // Identity Matrix	cQuaternion Qx, Qy, Qz;	Qx.FromAxisAngle(I[0], A.x); // X Axis	Qy.FromAxisAngle(I[1], A.y); // Y Axis	Qz.FromAxisAngle(I[2], A.z); // Z Axis	(*this) = Qx * Qy * Qz; // Combine rotations	(*this).Normalize(); 	return;}


Then I just convert this back to a matrix using this:

void cMatrix3::operator = (cQuaternion Q){	Q.Normalize();	R[0].x = 1.0f - (2.0f * (Q.V.y * Q.V.y)) - (2.0f * (Q.V.z * Q.V.z));	R[0].y = 2.0f * Q.V.x * Q.V.y - 2.0f * Q.V.z * Q.R;	R[0].z = 2.0f * Q.V.x * Q.V.z + 2.0f * Q.V.y * Q.R;	R[1].x = 2.0f * Q.V.x * Q.V.y + 2.0f * Q.V.z * Q.R;	R[1].y = 1 - (2.0f * (Q.V.x * Q.V.x)) - (2.0f * (Q.V.z * Q.V.z));	R[1].z = 2.0f * Q.V.y * Q.V.z - 2.0f * Q.V.x * Q.R;	R[2].x = 2.0f * Q.V.x * Q.V.z - 2.0f * Q.V.y * Q.R;	R[2].y = 2.0f * Q.V.y * Q.V.z + 2.0f * Q.V.x * Q.R;	R[2].z = 1.0f - (2.0f * (Q.V.x * Q.V.x)) - (2.0f * (Q.V.y * Q.V.y));	return;}


Wondering if this is an all right way to go about it?

Cheers.
Mark
Hi,

Just getting ready to turn in, but here are a few thoughts...

First, as you may already know, 'return' isn't really necessary at the end of your functions unless there's a return value.

Also, I assume your matrix constructor initializes to identity.

It's conventional for the scalar component of a quaternion to be labeled w. It's certainly no big deal, but it would make it easier (for you and others) to compare your code to other code and references.

Your quat mult function isn't shown here but I'm assuming it's correct.

As for your FromEuler() function, it looks like it should do exactly the same thing as a similar 'Euler-to-matrix' function would do. Keep in mind that the standard quaternion multiplication form [w1v2+w2v1+v1Xv2, w1w2-v1v2] results in the multiplication q1q2 applying rotation q2 first, and then q1. So in your function the rotations are probably being applied in the order z, y, x, which may or may not be what you want.

In at least one reference I've seen, the cross product terms v1Xv2 in the quaternion multiplication are reversed, i.e. v2Xv1. This has the effect of applying the rotation q1q2 in the order q1, q2. If you're using this form for your quaternion multiplication, then the rotations are in fact being applied in the order x, y, z.

All the terms of your quat-to-matrix function look correct. I'm assuming that you intend R[0], R[1], and R[2] to be the basis vectors of the coordinate system. If so, your matrix is transposed from what I would expect. I would think that for your matrix, the handedness of the rotation would be the opposite of the handedness of the coordinate system, e.g. if your coordinate system is left-handed, your rotations will be right-handed and vice versa. It really doesn't matter - it's just something to be aware of.

The most important thing here is probably that I don't think this approach will actually offer any *improvement* over Euler angles - you'll still suffer from the same problems, such as gimbal lock. Again, a solution is to rotate incrementally, as described in an above post.

A disclaimer: it's late, and I can't guarantee the accuracy of any or all of the above. But at least it might give you a few things to consider.

This topic is closed to new replies.

Advertisement