Sign in to follow this  
Cygon

Perpendicular Vector Calculation

Recommended Posts

Cygon    1219
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()?

Share this post


Link to post
Share on other sites
smitty1276    560
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.

Share this post


Link to post
Share on other sites
Cygon    1219
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?

Share this post


Link to post
Share on other sites
Cygon    1219
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!

Share this post


Link to post
Share on other sites
smitty1276    560
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. :-)


Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this