Sign in to follow this  
DrGUI

Look-at quaternion

Recommended Posts

DrGUI    402
Hi! Can some fantastic, brilliantly clever genius tell me how to make a look-at quaternion please? What I need is a quaternion that will rotate an object, e.g. an arrow, to point down a vector, assuming that with no rotation the arrow points down the z axis. It would be nice if someone knew how to specify a roll angle in radians along that vector too, however I won't push my luck now. I have got something working for now: working out the yaw and pitch angles with arctan and arcsin and passing them with the roll to my Yaw-Pitch-Roll function. However, this is very ugly accounting for where vector.X or Z is negative, and it must be much slower than it could be with all the trig involved. I will be most grateful - thanks!

Share this post


Link to post
Share on other sites
sbroumley    283
Something like this should do the trick:



// This is your identity (no rotation) axis - Z in your case
vector3 IdentityAxis( 0.0f, 0.0f, 1.0f );

// Compute cosine of angles between your identity axis and direction you want to face
Direction.Normalize();
float CosAngle = Dot( IdentityAxis, Direction );
if( CosAngle < -1.0f )
CosAngle = -1.0f;
else if( CosAngle > 1.0f )
CosAngle = 1.0f;

// Compute angle
float Angle = acos( CosAngle );

// Compute axis to rotate about
// TO DO: Check for Direction being the IdentityAxis
vector3 Axis = Cross( IdentityAxis, Direction );
Axis.Normalize();

// We now know the angle and axis to rotate around
// This could be used to construct a quaternion or matrix etc.
quaternion Q;
Q.SetupFromAxisAndAngle( Axis, Angle );


Share this post


Link to post
Share on other sites
DrGUI    402
You don't need to clamp the dot product if you've got 2 normalized vectors.
Look at www.geometrictools.com in the math section I think to find a quaternion function called 'Align', which handles parellel vectors.

Share this post


Link to post
Share on other sites
JohnBolton    1372
Quote:
Original post by DrGUI
You don't need to clamp the dot product if you've got 2 normalized vectors.


You have to clamp the dot product because it can result in values outside of -1 to 1, due to precision error, and then acos() will return NaN.

This is a frequent problem. If NaN is appearing in your results, the first thing to look for is the very common mistake of not clamping the parameter to acos() or asin().

Share this post


Link to post
Share on other sites
sbroumley    283
I agree with John. If you don't clamp, precision errors can cause the dot product to be < -1 or > 1 - which causes acos() to be undefined. Try if for yourself if you don't believe us :)

Share this post


Link to post
Share on other sites
DrGUI    402
Here's mine now:

/// <summary>
/// Computes a quaternion that rotates unit-length vector v1 to unit-length vector v2. The rotation is about
/// the axis perpendicular to both v1 and v2, with angle of that between v1 and v2. If v1 and v2 are parallel,
/// any axis of rotation is used, such as the permutation (v2.Z,v2.X,v2.Y).</summary>
/// <param name="v1">Normalized vector that the computed quaternion rotates from to v2.</param>
/// <param name="v2">Normalized vector that the computed quaternion rotates to from v1.</param>
/// <returns>A quaternion that rotates v1 to v2.</returns>
public static Quaternion Align(Vector3 v1, Vector3 v2)
{
#if DEBUG
float sqrLen1 = v1.LengthSq();
float sqrLen2 = v2.LengthSq();
System.Diagnostics.Debug.Assert(sqrLen1 > 1 - ZeroTolerence && sqrLen1 < 1 + ZeroTolerence &&
sqrLen2 > 1 - ZeroTolerence && sqrLen2 < 1 + ZeroTolerence,
MsgVecNonNormalized);
#endif
// If V1 and V2 are not parallel, the axis of rotation is the unit-length
// vector U = Cross(V1,V2)/Length(Cross(V1,V2)). The angle of rotation,
// A, is the angle between V1 and V2. The quaternion for the rotation is
// q = cos(A/2) + sin(A/2)*(ux*i+uy*j+uz*k) where U = (ux,uy,uz).
//
// (1) Rather than extract A = acos(Dot(V1,V2)), multiply by 1/2, then
// compute sin(A/2) and cos(A/2), we reduce the computational costs by
// computing the bisector B = (V1+V2)/Length(V1+V2), so cos(A/2) =
// Dot(V1,B).
//
// (2) The rotation axis is U = Cross(V1,B)/Length(Cross(V1,B)), but
// Length(Cross(V1,B)) = Length(V1)*Length(B)*sin(A/2) = sin(A/2), in
// which case sin(A/2)*(ux*i+uy*j+uz*k) = (cx*i+cy*j+cz*k) where
// C = Cross(V1,B).
Vector3 bisector = v1 + v2;
bisector.Normalize();
float cosHalfAngle = Vector3.Dot(v1, bisector);
Vector3 cross;
if (cosHalfAngle > ZeroTolerence || cosHalfAngle < -ZeroTolerence) {
cross = Vector3.Cross(v1, bisector);
} else {
cross = Vector3.Cross(v1, new Vector3(v2.Z, v2.X, v2.Y));
//cosHalfAngle = 0.0f; - already is very close
//cross.Normalize();
}

#if DEBUG
float sqrLen = cross.X*cross.X+cross.Y*cross.Y+cross.Z*cross.Z+cosHalfAngle*cosHalfAngle;
System.Diagnostics.Debug.Assert(sqrLen > 1 - ZeroTolerence && sqrLen < 1 + ZeroTolerence);
#endif
return new Quaternion(cross.X, cross.Y, cross.Z, cosHalfAngle);
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
Good work - I do this by creating a lookat matrix then use that matrix as a source for a quaternion.

Share this post


Link to post
Share on other sites
Dmytry    1151
DrGUI:
Your version will fail on antiparallel vectors anyway, truing to normalize bisvector. Also it can fail in some other cases, your bad-case cross will not always work. (it will not work with vectors (anti-)parallel to 1,1,1 )

I think,

Vector3 cross;
Vector3 bisector = v1 + v2;
float len = bisvector.Length(); // length is equal to 2*cosine of half angle
if(l<very_small_value){// rare bad case: vectors is parallel
cross=Vector3.Cross(v1, Vector3(1, 0, 0));
len=cross.Length();
if(len>0.00001) return Quaternion(cross.X/len, cross.Y/len, cross.Z/len, 0);// rotate 180 degrees around "cross"
return Quaternion(0,1,0,0);// vector is paralel to 1,0,0 ; so rotate around 0,1,0 by 180 degrees
}
cross = Vector3.Cross(v1, bisector);
float tmp=1.0/len;
return Quaternion(tmp*cross.X, tmp*cross.Y, tmp*cross.Z, 0.5*len);




must work better and faster.(normally, only one square root and one divide)

(Another note, it is IMHO better standard to have quaternion constructor take things in that order: w,x,y,z (in math, it's usually written as w+x*i+y*j+z*k) . (i wrote code compatible with your))

Share this post


Link to post
Share on other sites
Dmytry    1151
from minorlogic's article:
x = (v1 x v2).x
y = (v1 x v2).y
z = (v1 x v2).z
w = 1 + v1•v2
and then normalize quaternion. Very cool thing.

Share this post


Link to post
Share on other sites
DrGUI    402
Quote:
Original post by Dmytry
from minorlogic's article:
x = (v1 x v2).x
y = (v1 x v2).y
z = (v1 x v2).z
w = 1 + v1•v2
and then normalize quaternion. Very cool thing.

If the x's are cross products I don't think that would work with parellel vectors becaue then the cross product is undefined.

EDIT: I haven't had time to try it yet though.

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