How to re-orthogonalise vectors

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

If I have three vectors which are initially perpendicular to eachother, how can I re-orthogonalise them when I rotate them to make sure they're still perpendicular? I guess it has something to do with checking that the dot product is zero, but I'm not sure beyond that. Thanks!

Advertisement

If they were ok before you rotated them, then they should be ok after you rotate them, except for really small numerical errors. These errors are only a problem if you keep applying rotations to a matrix frame after frame, instead of rebuilding it from scratch each time, so most of the time this isn't required.

If it is required, you can use the cross product to find a vector that's perpendicular to two others. Pick 2 of your vectors and cross them to regenerate the 3rd. Then use the 1st and the 3rd to regenerate the 2nd. Each time you regenerate one, you can check it's dot product with the original version; it should be very close to 1.0 or -1.0. If it's negative, then flip the regenerated vector so it matches the direction/handedness of the original.

If you rotate all three vectors by the same rotation matrix (or any other method), they should preserve their orthogonality.

If you're interested in just verifying that they're still orthogonal, it is as simple as checking the three dot products to see if they're equal to 0.

If you want to re-orthogonalize them, you have to do a bit more work. First, you can't arbitrarily re-orthogonalize them.

here's two general cases, given the three potentiall non-orthogonal vectors A, B and C. (these are the vectors after rotation here)

First if A and B are perpendicular ( or A and C, or B and C - these are all equivalent cases), you can calculate the third vector by doing

C = A cross B or B cross A - depending on handedness. Same goes for the case if A and C are perpendicular, or if B and C are perpendicular.

Second general case is if no two vectors are perpendicular to each other. If that's the case, you have to pick one vector which you want to remain unchanged - usually this is one vector who's orientation you know is correct.

Let's say you pick vector A as the 'correct' one.

You can then obtain a new orthogonal vector B' by doing

B' = A cross C

even though C is not perpendicular to A, A cross C will yield a vector that IS perpendicular to A (as well as to C)

Then you can obtain the new C' by doing

C' = A cross B'

In that case your three new vectors are A, B' and C' - which are now orthogonal. You'll also have to keep track of handedness of the system, because otherwise you might end up with a B' or C' that is oriented in the opposite direction of the originals.

Same goes if you pick B and C, just substitute the proper letters above.

Mind that you might have to re-normalize the vectors if you want to keep them unit and if there's a chance that the rotated vectors are no longer of unit length.

My setup at the moment is that I have an 'orientation' matrix which I update every frame by an incremental amount. I am using it to control the yaw, pitch and roll of an object about its local axes, each of which are defined by a vector (i.e. I have an up, forward and right vector set to define the orientation of the axes). It's these axes that I'm trying to ensure remain perpendicular to eachother. I have to rotate each vector by a different matrix because they are each rotating around eachother (i.e. to pitch forward, I have to rotate around my 'right' vector, then to roll left I have to rotate about my new 'forward' vector, so they all depend on eachother). It's pretty awkward, but it seems to work.

I'll take my 'up' axis as always being correct and regenerate each axis around that one. I checked and the deviations are very small (a tiny fraction of a degree which gradually increases over time), but I'd rather do it properly and re-orthogonalise them each time.

If you store the orientation as a quaternion, it's as simple as re-normalizing the quaternion.

void normalize()
{
/*
Column1=Normalized(CrossProduct(Column2,Column3));
Column2=Normalized(CrossProduct(Column3,Column1));
Column3=Normalized(Column3);
Prot is global player's rotation matix
*/
D3DXVECTOR3 v1,v2,v3, // 3 original axes
v4,v5,v6; // 3 ortho normal axes
v1.x=Prot._11;
v1.y=Prot._21;
v1.z=Prot._31;
v2.x=Prot._12;
v2.y=Prot._22;
v2.z=Prot._32;
v3.x=Prot._13;
v3.y=Prot._23;
v3.z=Prot._33;
D3DXVec3Cross(&v4,&v2,&v3);
D3DXVec3Normalize(&v4,&v4);
D3DXVec3Cross(&v5,&v3,&v4);
D3DXVec3Normalize(&v5,&v5);
D3DXVec3Normalize(&v6,&v3);
Prot._11=v4.x;
Prot._21=v4.y;
Prot._31=v4.z;
Prot._12=v5.x;
Prot._22=v5.y;
Prot._32=v5.z;
Prot._13=v6.x;
Prot._23=v6.y;
Prot._33=v6.z;
}
this is gram-schmitt re-ortho-normalization
i usually assume the forward vector (local z) to be the correct one. this will eliminate "course drift" due to re-normalization. IE any change in orientation due to re-normalization shows up as a change in roll, not heading.
this still doesn't get you pixel perfect rotations at 1600 x 900 with floats. very close, but you don't come back to exactly the same pixel every time when you do a 360 around a local axis when the ship is at an arbitrary starting orientation.
I suspect quats are really the way to go for 3 axes of rotational freedom.
but i have done flight sims using orientation matrices, and they do work pretty well, if you can get them accurate enough.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

In prep for a new title planned, i revisited the "how to store orientation" question.
I found that the graphics engine uses matrices, movement uses a direction vector, AI (at least mine) uses Euler angles, and turning is most easily done as a matrix multiply or a quaternion multiply. turning has issues with floating point precision and matrices. the REAL question is: are floating point quats more accurate than floating point mats ? i suspect the answer is no. in any case, D3DX has math routines to convert most types to a matrix, and can convert between mats and quats. as for mats vs quats, assuming both are equally inaccurate, quats are less work to re-normalize, maybe. their big bonus seem to be in the ability to easily tween for animation purposes. great if you're doing canned ani's or tweening between last turn's and this turn's position in a frame independent system (which would always cause the graphics to lag behind the simulation, wouldn't it?).
in the end, i decided that for the moment, ortho-normalized mats are the way to go to store orientation. you can pass it directly to the graphics engine, the 3rd column vector is your local z axis for movement, and a mat mul followed by the above normalize() routine handles turning within the limits of the precision implemented. it may be possible to store orientation in a long double mat, use that for turning (have to roll your own long double mat mul), then copy the results to a D3DXMATRIX for drawing. but inaccuracies would still eventually creep in. when storing a rotation in radians, a float gets you 6 decimal places of accuracy, a long double gets you 14. with a float you can do maybe 3 360's before your axes are totally whacked.
I considered storing orientation as eulers, using mats to turn, then extracting eulers from the mat. but irregardless of what data structure holds the orientation, you have to use floating point matrix multiply or quaternion multiply to turn. so imprecision will most likely still be an issue. given that every time i see postings on this topic, those of the "quat camp" say "just renormalize" (witness above in this thread), odd are quats are no more precise and also need to need re-normalized. a quick test will determine the truth once and for all. i've seen this issue of how to store orientation debated on formus since compuserv gamedev back in the late 80's. I would think by now the correct answer would be general gamedev knowledge. but 3 degree rotational freedom still seems to be a black art to most gamedevs, probably due to the fact that its only required for the most complex simulators.
perhaps i'll take a little time out and test mats vs quats. i have a flight sim engine prototype just for testing such things. it was built to test normalize().
i wonder where the drift shows up in a quat. given that a quat conglomerates a unit direction vector and a rotation into a V4, i suspect its spread over all directions.
has anyone out there used quats? are they more accurate? can you get pixel perfect 360's at arbitrary rotations with quats?
while thinking about it, i determined that about the only thing not in D3DX required for using matrices is a routine to extract Euler angles from a rotation matrix, for use by AI (heading to target, relative heading to target, in arc of fire, etc).
My first big hit game was a space flight sim. it too used fwd, up and right unit vectors to store orientation. it was written before directx existed.
here's how to get Euler angles from a matrix:
ROTATION MATRIX TO HEADING AND ANGLE:
rotate v1=0,0,1 by ship's rotation matrix to get V2. // this rotates the local z axis out to v2 where it should be pointing.
heading =atan(v2.x/v2.z) // heading is calculated from the projection of v2 onto the x-z plane.
rotate v2 by -heading just calculated to get v3 // this rotates v2 back into the y-z plane, yielding v3 .
angle = -atan(v3.y/v3.z) // angle of inclination is calculated from v3, which is v2 rotated back to the y-z plane. note the negative sign. x rotation is down, angle is up.
so you store your orientation as a mat, use normalize after a turn, use your 3rd column (local z, or k unit vector) * speed = your displacement vector for moving, pass the mat directly to directx for drawing, and use matrix to heading/angle to get angles for AI calculations.
eventually i'll have to test quats. when i do i'll post the results. right now i'm bogged down in designing quest/mission generators for another title.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

From another thread here

http://www.gamedev.net/topic/640889-pixel-perfect-local-rotations-and-quats-vs-mats/

i've learned that quats also have precision errors, but they do not accumulate as quickly.

it looks like fixed point is the way to do it, then convert to mat for drawing.

that would mean rolling your own fixed point versions of mat mul or whatever method used to turn.

error would eventually creep in most likely.

looks like re-ortho normalize is still required.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Everything has precision errors.

All that's required if you store orientation as a quaternion is renormalising once per frame. When you construct a matrix from the quat it will be orthonormal (within accuracy limits).

Error only "creeps in" if you never renormalise or reorthonormalise.

Using fixed point won't solve anything, don't roll your own fixed point functions just to get rounding issues and worse performance.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley
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?

This topic is closed to new replies.

Advertisement