# 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.