Im struggling with the following problem. Im working with bone animation and I want (ie) the head of the player to follow an another object in space. My up axis is +Z my forward axis is +Y, and the magnitude of the quaternion is in W. I tried to use the mesa code for gluLookAt and use the 3x3 matrix to transform to a quaternion but it doesn't work as expected so I go in another direction...
So far I got the following code that is "almost" working at least the head of the player is rotating (however the X angle seems to affect the Y rotation axis) in the good direction but its looking straight up instead on following an object on the floor at about 65 degree:
qt LookRotation( v3 lookAt, v3 upDirection )
{
qt t;
v3 forward = lookAt;
v3 up = upDirection;
OrthoNormalize( &forward, &up );
v3 right = v3_cross( up, forward );
mat3 m = mat3_make( right.x, up.x, forward.x,
right.y, up.y, forward.y,
right.z, up.z, forward.z );
t.w = sqrtf( 1.0f +
m.r[ 0 ].x +
m.r[ 1 ].y +
m.r[ 2 ].z ) * 0.5f;
float w4_recip = 1.0f / ( 4.0f * t.w );
t.x = ( m.r[ 2 ].y - m.r[ 1 ].z ) * w4_recip;
t.y = ( m.r[ 0 ].z - m.r[ 2 ].x ) * w4_recip;
t.z = ( m.r[ 1 ].x - m.r[ 0 ].y ) * w4_recip;
t = qt_normalize( t );
return t;
}
... ... ...
v3 v = v3_sub( vec4_to_v3( transform.world.r[ 3 ] /* The object XYZ location in the world */),
skeleton->final_pose.location[ i ] /* i = The head joint location */ );
v = v3_normalize( v );
qt q = LookRotation( v,
v3_make( 0.0f, 0.0f, 1.0f ) );
Can someone help me figuring out this problem... Im kinda new with quaternions and don't really know where I could have messed up. After quite some research basically what I want to do is something like the Unity API: http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.LookRotation.html
I think this function will do what you need:
/// <summary>
/// Evaluates a rotation needed to be applied to an object positioned at sourcePoint to face destPoint
/// </summary>
/// <param name="sourcePoint">Coordinates of source point</param>
/// <param name="destPoint">Coordinates of destionation point</param>
/// <returns></returns>
public static Quaternion LookAt(Vector3 sourcePoint, Vector3 destPoint)
{
Vector3 forwardVector = Vector3.Normalize(destPoint - sourcePoint);
float dot = Vector3.Dot(Vector3.forward, forwardVector);
if (Math.Abs(dot - (-1.0f)) < 0.000001f)
{
return new Quaternion(Vector3.up.x, Vector3.up.y, Vector3.up.z, 3.1415926535897932f);
}
if (Math.Abs(dot - (1.0f)) < 0.000001f)
{
return Quaternion.identity;
}
float rotAngle = (float)Math.Acos(dot);
Vector3 rotAxis = Vector3.Cross(Vector3.forward, forwardVector);
rotAxis = Vector3.Normalize(rotAxis);
return CreateFromAxisAngle(rotAxis, rotAngle);
}
// just in case you need that function also
public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle)
{
float halfAngle = angle * .5f;
float s = (float)System.Math.Sin(halfAngle);
Quaternion q;
q.x = axis.x * s;
q.y = axis.y * s;
q.z = axis.z * s;
q.w = (float)System.Math.Cos(halfAngle);
return q;
}
This code comes from here: https://gamedev.stackexchange.com/questions/15070/orienting-a-model-to-face-a-target I just slightly modified it to fit my case, which was implementation of the transform.LookAt without using Unity3D.
You don't need to use acos
and axis angle
(which will in turn do 2 more trig functions) to get the quaternion from 2 vectors:
public static Quaternion LookAt(Vector3 sourcePoint, Vector3 destPoint)
{
Vector3 forwardVector = Vector3.Normalize(destPoint - sourcePoint);
Vector3 rotAxis = Vector3.Cross(Vector3.forward, forwardVector);
float dot = Vector3.Dot(Vector3.forward, forwardVector);
Quaternion q;
q.x = rotAxis.x;
q.y = rotAxis.y;
q.z = rotAxis.z;
q.w = dot+1;
return q.normalize();
}
The reason for the dot+1 and subsequent normalize is because if you don't, you'll get the quaternion for the double rotation. Those 2 steps will effectively do slerp(identity, q, 0.5)
which will be the proper quaternion.
Both of current answers have various problems for edge cases. The accepted answer is not correct for other reasons as well including the fact that it sets w=pi for one of the cases and also it doesn't do proper norms. After looking around quite a bit and testing several cases, I also found out that you need front and up vector to do this computation. So without further ado below is the code I'm using:
Quaternion lookAt(const Vector3f& sourcePoint, const Vector3f& destPoint, const Vector3f& front, const Vector3f& up)
{
Vector3f toVector = (destPoint - sourcePoint).normalized();
//compute rotation axis
Vector3f rotAxis = front.cross(toVector).normalized();
if (rotAxis.squaredNorm() == 0)
rotAxis = up;
//find the angle around rotation axis
float dot = VectorMath::front().dot(toVector);
float ang = std::acosf(dot);
//convert axis angle to quaternion
return Eigen::AngleAxisf(rotAxis, ang);
}
Bove uses popular Eigen library. If you don't want to use that then you might need following replacement for Eigen::AngleAxisf
:
//Angle-Axis to Quaternion
Quaternionr angleAxisf(const Vector3r& axis, float angle) {
auto s = std::sinf(angle / 2);
auto u = axis.normalized();
return Quaternionr(std::cosf(angle / 2), u.x() * s, u.y() * s, u.z() * s);
}
Note that special cases for dot product 0 or 1 or -1 gets automatically handled because normalized() returns 0 for the zero vector in Eigen library.
On the side note, for all your conversions worries, this is a great document to go to.
来源:https://stackoverflow.com/questions/12435671/quaternion-lookat-function