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!

**0**

###
#2
Moderators - Reputation: **29302**

Posted 13 March 2013 - 09:08 AM

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.

**Edited by Hodgman, 13 March 2013 - 09:18 AM.**

###
#3
Crossbones+ - Reputation: **1175**

Posted 13 March 2013 - 09:09 AM

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.

###
#4
Members - Reputation: **1185**

Posted 13 March 2013 - 09:34 AM

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.

###
#6
Crossbones+ - Reputation: **2033**

Posted 15 March 2013 - 09:50 AM

###
#7
Crossbones+ - Reputation: **2033**

Posted 24 March 2013 - 01:43 PM

###
#8
Crossbones+ - Reputation: **2033**

Posted 01 April 2013 - 03:00 PM

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.

###
#9
Crossbones+ - Reputation: **5250**

Posted 01 April 2013 - 03:09 PM

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.

###
#10
Members - Reputation: **1185**

Posted 01 April 2013 - 04:39 PM

###
#11
Crossbones+ - Reputation: **2033**

Posted 06 April 2013 - 10:00 PM

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.

###
#12
Members - Reputation: **1185**

Posted 07 April 2013 - 03:27 AM

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?

###
#13
Crossbones+ - Reputation: **4146**

Posted 07 April 2013 - 04:20 AM

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.

**Edited by haegarr, 07 April 2013 - 04:20 AM.**

###
#14
Members - Reputation: **1185**

Posted 07 April 2013 - 09:29 AM

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?

###
#15
Crossbones+ - Reputation: **2033**

Posted 07 April 2013 - 09:45 AM

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.

###
#16
Crossbones+ - Reputation: **4146**

Posted 07 April 2013 - 10:03 AM

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:

OrientationMatrix_{n+1} = OrientationMatrix_{n} * matPitch * matYaw * matRoll

But you compute the new vectors as:

OrientationVectors_{n+1} = OrientationVectors_{n} * 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.

###
#18
Members - Reputation: **1185**

Posted 07 April 2013 - 02:11 PM

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.

**Edited by george7378, 07 April 2013 - 02:11 PM.**

###
#19
Moderators - Reputation: **29302**

Posted 07 April 2013 - 03:48 PM

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.is it rows or columns? i can never remember. been way too long since i took linear.

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.

IIRC, D3DX uses the opposite convention than what mathematicians tend to use, for some strange reason.

###
#20
Crossbones+ - Reputation: **4901**

Posted 07 April 2013 - 04:10 PM

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.

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

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.

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.

Jason Zink :: DirectX MVP

Direct3D 11 engine on CodePlex: Hieroglyph 3

Direct3D Books: Practical Rendering and Computation with Direct3D 11, Programming Vertex, Geometry, and Pixel Shaders

Articles: Dual-Paraboloid Mapping Article :: Parallax Occlusion Mapping Article (original):: Fast Silhouettes Article

Games: Lunar Rift