Perpendicular Vector Calculation

Started by
4 comments, last by smitty1276 15 years, 4 months ago
I'm looking for a trick to build a 3x3 matrix from a single forward-pointing vector, similar to what your typical LookAt() function does, only that I need numerical stability and don't care about the orientation of the right and up vectors, as long as they're perpendicular. My first idea was to just switch the coordinates around, eg.
Matrix3x3 createQuickLookAtMatrix(const Vector3 &forward) {
  Vector3 up(forward.Y, forward.Z, forward.X);
  Vector3 right(forward.Z, forward.X, forward.Y);
  
  return Matrix3x3(right, up, forward);
}
This would work fine with axis aligned unit vectors (eg. straight up, straight right, straight forward :p) but wouldn't work for eg. a Vector(1, 1, 1) which would still be pointing in the exact same direction afterwards. So I thought of negating two of the vector components
Matrix3x3 createQuickLookAtMatrix(const Vector3 &forward) {
  Vector3 up(-forward.Y, -forward.Z, forward.X);
  Vector3 right(-forward.Z, forward.X, -forward.Y);
  
  return Matrix3x3(right, up, forward);
}
However, I'm not sure whether this trick will always work. Can anyone come up with a vector for which it doesn't work out? Is there another shortcut to obtain such a vector without resorting to something like OrthoNormalize()?
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Advertisement
if Look == <0,1,0> then make up a good Right and Up

otherwise:
Right = Look x <0,1,0>
Up = Right x Look
BTW, your final method there doesn't work for an input of <1,1,1>. You get up and right vectors of <-1,-1,1> and <-1,1,-1>, giving you 3 vectors that are 109.47 degrees apart from one another.
Ah, nevermind, I stumbled on this old thread where jyk and alvaro provided the exact answer I was looking for.

The two code snippets I've now come up with are

Alvaro's method:
Matrix3x3 createQuickLookAtMatrix(const Vector3 &forward) {  Vector3 right;  if(abs(vector.X) < abs(vector.Y)) {    if(abs(vector.Z) < abs(vector.X)) {      right = Vector3(vector.Y, -vector.X, 0.0f);    } else {      right = Vector3(0.0f, vector.Z, -vector.Y);    }  } else {    if(abs(vector.Z) < abs(vector.Y)) {      right = Vector3(vector.Y, -vector.X, 0.0f);    } else {      right = Vector3(vector.Z, 0.0f, -vector.X);    }  }  Vector3 up = Vector3.Cross(forward, right);  return Matrix3x3(right, up, forward);}


Jyk's method:
Matrix3x3 createQuickLookAtMatrix(const Vector3 &forward) {  Vector3 crossVector;  if(abs(vector.X) < abs(vector.Y)) {    if(abs(vector.Z) < abs(vector.X)) {      crossVector = Vector3.UnitZ;    } else {      crossVector = Vector3.UnitX;    }  } else {    if(abs(vector.Z) < abs(vector.Y)) {      crossVector = Vector3.UnitZ;    } else {      crossVector = Vector3.UnitY;    }  }  Vector3 right = Vector3.Cross(vector, crossVector);  Vector3 up = Vector3.Cross(vector, right);  return Matrix3x3(right, up, forward);}


I don't quite understand yet why alvaro's method seems to work, but if it's safe, maybe a permutation of it could also yield the 'up' vector without resorting to a cross product at all.

Some unit tests with unit vectors, diagonal vectors and more seem to indicate it works.

So, back to the original question, can anything think of a vector for which alvaro's method wouldn't work?
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Quote:Original post by smitty1276
if Look == <0,1,0> then make up a good Right and Up

otherwise:
Right = Look x <0,1,0>
Up = Right x Look


Thanks, but that's what I tried to avoid for reasons of numerical stability. If the vector was very close to (0, 1, 0) but not identical, the cross product might be slightly off due to rounding errors.

Quote:Original post by smitty1276
BTW, your final method there doesn't work for an input of <1,1,1>. You get up and right vectors of <-1,-1,1> and <-1,1,-1>, giving you 3 vectors that are 109.47 degrees apart from one another.


Of course! Well, I guess I can scrap that whole idea then. If I negate just one element, there is at least one unit vector for which it wouldn't work. Good find!
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Quote:Original post by Cygon
Thanks, but that's what I tried to avoid for reasons of numerical stability. If the vector was very close to (0, 1, 0) but not identical, the cross product might be slightly off due to rounding errors.
Ah! I wasn't sure what you meant by that... it seems like it would be a cheap operation, though, to just check for a vector within some epsilon of 0,1,0, to avoid the instability.

But if your purposes are served by the other method, there's no point really. :-)


This topic is closed to new replies.

Advertisement