How to re-orthogonalise vectors

Started by
19 comments, last by Norman Barrows 11 years ago

Hey - I'm interested in using quaternions, but I don't see how I'd do it - at the moment I use 3 vectors to represent up, forward and right, and I rotate incrementally about those axes while also rotating them each time (it's a local yaw, pitch and roll transformation). How could I do local rotations with quaternions?

same as with a matrix (at least in D3D)

orientation = local_rotation * orientation. but you use quat mul instead of mat mul. check the docs, it in there, D3DX reference, math functions, as i recall.

using quats should cause error to accumulate more slowly. but quats or mats, you'll still need to normalize from time to time.

you can use quat to mat routine to get your mat for drawing, and to get your column vectors (i,j,k or right,up,fwd) for movement, or to use the above code to convert to Eulers if needed, etc.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement

I've read a bit about them and it seems like they hold 4 values only - an axis and an angle. What I don't understand is how I can use a quaternion to hold the entire orientation of my object? Surely I'd still have to use my three local axis vectors, and hence I'd still have the same problems of keeping them orthogonal?

I've read a bit about them and it seems like they hold 4 values only - an axis and an angle.

Yes: Quaternions are defined by 4 scalars. No: They don't hold an axis and an angle. There is another rotation representation that is called "axis/angle representation" and that actually hold, well, an axis and an angle. IMHO it is best not to try to interpret some geometric meaning into quaternions; look at them as being a mathematical construct in your toolbox.

In fact, each representation has some degree of freedom: In 3D, a rotation matrix has 9, a quaternion has 4, an axis/angle has 4. Because rotation only has 3, you need to enforce some constraints on the representations, or else your representation doesn't mean a pure rotation.

In the case of rotation matrices the column/row vectors are pairwise orthogonal and each one is a unit vector. Hence the process of "re-ortho-normalization", which carries both orthogonalization as well as normalization in its name.

In the case of quaternions they need to be of unit length. Hence, to be precise, one has to talk about "unit-quaternions" and not just quaternions when referring to pure rotation.

Because of this, quaternions are not less prone to numerical errors per se, but they are more stable in that numerical imprecision has less effect on still representing a useable rotation. So re-normalisating quaternions needs to be done less often, and ensuring that single constraint is less work than ensuring the 6 constraints on rotation matrices.

That said, using quaternions and re-normalization (from time to time) and on-the-fly to-matrix conversion would be sufficient for sure.

What I don't understand is how I can use a quaternion to hold the entire orientation of my object?

The problem of lacking a geometrical interpretation suitable for humans, doesn't mean that unit-quaternions don't work. A unit-quaternions "holds the entire orientation" by its nature. Use conversions from or into, resp., other rotation representations where meaningful.

OK, so here's my code at the moment for doing local axis rotations:

D3DXVec3Cross(&right, &up, &forward); //Reorthogonalise right
D3DXVec3Cross(&forward, &right, &up); //Reorthogonalise forward
D3DXVec3Normalize(&up, &up); D3DXVec3Normalize(&right, &right); D3DXVec3Normalize(&forward, &forward); //Renormalise the vectors
 
D3DXMATRIX matYaw; D3DXMatrixRotationAxis(&matYaw, &up, OrientationDelta.x); //Build a yaw rotation axis
D3DXMATRIX matPitch; D3DXMatrixRotationAxis(&matPitch, &right, OrientationDelta.y); //Build a pitch rotation axis
D3DXMATRIX matRoll; D3DXMatrixRotationAxis(&matRoll, &forward, OrientationDelta.z); //Build a roll rotation axis
 
D3DXVec3TransformCoord(&forward, &forward, &matYaw); D3DXVec3TransformCoord(&right, &right, &matYaw); //Apply the yaw transformation to the relevant vectors
D3DXVec3TransformCoord(&forward, &forward, &matPitch); D3DXVec3TransformCoord(&up, &up, &matPitch); //Apply the pitch transformation to the relevant vectors
D3DXVec3TransformCoord(&right, &right, &matRoll); D3DXVec3TransformCoord(&up, &up, &matRoll); //Apply the roll transformation to the relevant vectors

OrientationMatrix *= matPitch*matYaw*matRoll; //Adjust the orientation matrix

So I use 3 vectors - up, right and forward, and rotate them by a small amount using matrix rotation axes. I don't see how using quaternions would change this. I'd still have to store the three axis vectors, I'd still have to rotate them a small amount each frame, and I'd still have to build a final orientation matrix to rotate my model. Would it help with re-orthogonalising and normalising? What would it improve?

What I don't understand is how I can use a quaternion to hold the entire orientation of my object?

if you look at the definition of a quat, you'll see that they sort of kludge the angle and i,j,k components together. thus they do encapsulate a direction vector and a roll. but as haegarr said, they do not have a direct geometric interpretation that allows one to determine values by inspection, the way you can look at the components of your forward vector, picture in your mind what direction that is and say "ok, i'm pointing this way". actually, i have yet to inspect the definition in detail, perhaps they do have some semi-intuitive geometric representation in something like spherical coordinates or something.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Let aside whether using quaternions makes sense in your use case, IMO your code snippet shows a construction flaw:

...
D3DXVec3TransformCoord(&forward, &forward, &matYaw); D3DXVec3TransformCoord(&right, &right, &matYaw); //Apply the yaw transformation to the relevant vectors
D3DXVec3TransformCoord(&forward, &forward, &matPitch); D3DXVec3TransformCoord(&up, &up, &matPitch); //Apply the pitch transformation to the relevant vectors
D3DXVec3TransformCoord(&right, &right, &matRoll); D3DXVec3TransformCoord(&up, &up, &matRoll); //Apply the roll transformation to the relevant vectors

OrientationMatrix *= matPitch*matYaw*matRoll; //Adjust the orientation matrix

The 3 row vectors of a rotation (row) matrix already define the side, up, and forward vectors of the local frame w.r.t. the parental frame. That means that your vectors right, forward, and up are logically part of your OrientationMatrix.

Now, you compute the new OrientationMatrix as:

OrientationMatrixn+1 = OrientationMatrixn * matPitch * matYaw * matRoll

But you compute the new vectors as:

OrientationVectorsn+1 = OrientationVectorsn * matYaw * matPitch * matRoll

That is obviously not the same when remembering that matrix multiplications isn't commutative. Hence the both orientation representations you use will be different, although logically they should be the same.

The 3 row vectors of a rotation (row) matrix already define the side, up, and forward vectors of the local frame w.r.t. the parental frame.

is it rows or columns? i can never remember. been way too long since i took linear.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Hi guys,

haegarr - I just noticed that yes, there is an error in the order. I really don't know why! Anyway, I've corrected it now.

I also looked into what you both said about getting the vectors from the matrix, and it turns out that this works:


    up = D3DXVECTOR3(OrientationMatrix._21, OrientationMatrix._22, OrientationMatrix._23);
    forward = D3DXVECTOR3(OrientationMatrix._31, OrientationMatrix._32, OrientationMatrix._33);
    right = D3DXVECTOR3(OrientationMatrix._11, OrientationMatrix._12, OrientationMatrix._13);

...so it looks like the individual vectors are stored in the rows. At least I think that's how it is - am I right in saying that, for example, _12 corresponds to row 1, column 2? So it turns out that I don't need to store the vectors like I was doing - they're already there! Of course, that doesn't solve the problem of orthonormalising them, although I messed about a bit with quaternions and I came up with this:


    D3DXVECTOR3 up(OrientationMatrix._21, OrientationMatrix._22, OrientationMatrix._23);
    D3DXVECTOR3 forward(OrientationMatrix._31, OrientationMatrix._32, OrientationMatrix._33);
    D3DXVECTOR3 right(OrientationMatrix._11, OrientationMatrix._12, OrientationMatrix._13);
 
    D3DXQUATERNION quatYaw, quatPitch, quatRoll; //Quaternions to hold local rotation info
    D3DXQuaternionRotationAxis(&quatYaw, &up, OrientationDelta.x); //Build a yaw rotation axis
    D3DXQuaternionRotationAxis(&quatPitch, &right, OrientationDelta.y); //Build a pitch rotation axis
    D3DXQuaternionRotationAxis(&quatRoll, &forward, OrientationDelta.z); //Build a roll rotation axis
    OrientationQuat *= quatYaw*quatPitch*quatRoll; D3DXQuaternionNormalize(&OrientationQuat, &OrientationQuat);
    D3DXMatrixRotationQuaternion(&OrientationMatrix, &OrientationQuat);

Is this the sort of thing that I'm after? I removed the current orthogonalising routine I've got (cross products, etc...) and put in the D3DXQuaternionNormalize() part.



The 3 row vectors of a rotation (row) matrix already define the side, up, and forward vectors of the local frame w.r.t. the parental frame.

is it rows or columns? i can never remember. been way too long since i took linear.
it's either, depending on whether you pretend that a vec3 is a mat3x1 or a mat1x3. The correct transform matrices for each of those arbitrary conventions will be transposed compared to the other.
IIRC, D3DX uses the opposite convention than what mathematicians tend to use, for some strange reason.



The 3 row vectors of a rotation (row) matrix already define the side, up, and forward vectors of the local frame w.r.t. the parental frame.

is it rows or columns? i can never remember. been way too long since i took linear.
it's either, depending on whether you pretend that a vec3 is a mat3x1 or a mat1x3. The correct transform matrices for each of those arbitrary conventions will be transposed compared to the other.
IIRC, D3DX uses the opposite convention than what mathematicians tend to use, for some strange reason.

If you look at the equations that are in the D3D docs, it is always a vector on the left of the matrix it is being multiplied by. In mathematic terms, this means that you have something like these sizes:

1x4 * 4x4 = 1x4

Since there is only one row (the first number is the number of rows) in the vector, they are normally referred to as row vectors. OpenGL typically uses the opposite convention, so that can be confusing if you aren't watching for it.

This topic is closed to new replies.

Advertisement