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 on other sites
sbroumley    283
Something like this should do the trick:

// This is your identity (no rotation) axis - Z in your casevector3 IdentityAxis( 0.0f, 0.0f, 1.0f );// Compute cosine of angles between your identity axis and direction you want to faceDirection.Normalize();float CosAngle = Dot( IdentityAxis, Direction );if( CosAngle < -1.0f )    CosAngle = -1.0f;    else if( CosAngle > 1.0f )    CosAngle = 1.0f;    // Compute anglefloat Angle = acos( CosAngle );    // Compute axis to rotate about// TO DO: Check for Direction being the IdentityAxisvector3 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 on other sites
DrGUI    402
I'll try that - thanks!

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 on other sites
JohnBolton    1372
Quote:
 Original post by DrGUIYou 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 on other sites
DrGUI    402
Ahhh - precision - yeeesss

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 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 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 on other sites
minorlogic    150
http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm

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 angleif(l<very_small_value){// rare bad case: vectors is parallelcross=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 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 on other sites
DrGUI    402
Thanks a lot, I'll try that.

Share on other sites
DrGUI    402
Quote:
 Original post by Dmytryfrom minorlogic's article:x = (v1 x v2).x y = (v1 x v2).yz = (v1 x v2).zw = 1 + v1•v2and 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.

Create an account

Register a new account