Jump to content
  • Advertisement
Sign in to follow this  
DrGUI

Look-at quaternion

This topic is 4863 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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
Advertisement
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
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
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
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
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
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
http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!