四元数“lookAt”function

我正在努力解决以下问题。 我正在使用骨骼动画,我希望(即)玩家的头部跟随太空中的另一个物体。 我的上轴是+ Z我的前轴是+ Y,四元数的大小是W.我试图使用gluLookAt的mesa代码并使用3×3矩阵转换为四元数但它不能按预期工作所以我走向另一个方向……

到目前为止,我得到了以下代码“几乎”工作,至少玩家的头部正在旋转(但是X角度似乎影响Y旋转轴)在良好的方向上,但它直视向上而不是跟随一个物体约65度的地板:

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 ); tw = sqrtf( 1.0f + mr[ 0 ].x + mr[ 1 ].y + mr[ 2 ].z ) * 0.5f; float w4_recip = 1.0f / ( 4.0f * tw ); tx = ( mr[ 2 ].y - mr[ 1 ].z ) * w4_recip; ty = ( mr[ 0 ].z - mr[ 2 ].x ) * w4_recip; tz = ( mr[ 1 ].x - mr[ 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 ) ); 

有人可以帮助我解决这个问题……我有点新的四元数,并不知道我可能搞砸了哪里。 经过相当多的研究后,基本上我想做的就是Unity API: http : //docs.unity3d.com/Documentation/ScriptReference/Quaternion.LookRotation.html

我认为这个function可以满足您的需求:

 ///  /// Evaluates a rotation needed to be applied to an object positioned at sourcePoint to face destPoint ///  /// Coordinates of source point /// Coordinates of destionation point ///  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; qx = axis.x * s; qy = axis.y * s; qz = axis.z * s; qw = (float)System.Math.Cos(halfAngle); return q; } 

这段代码来自这里: https : //gamedev.stackexchange.com/questions/15070/orienting-a-model-to-face-a-target我只是稍微修改它以适应我的情况,这是变换的实现。 LookAt不使用Unity3D。

您不需要使用acosaxis angle (这将依次执行2个触发函数)来从2个向量中获取四元数:

 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; qx = rotAxis.x; qy = rotAxis.y; qz = rotAxis.z; qw = dot+1; return q.normalize(); } 

点+ 1和后续标准化的原因是因为如果不这样做,您将获得双旋转的四元数。 这两个步骤将有效地执行slerp(identity, q, 0.5) ,这将是正确的四元数。

目前的答案都存在边缘情况的各种问题。 由于其他原因,接受的答案也是不正确的,包括它为其中一个案例设置了w = pi,而且它没有做正确的规范。 在仔细观察并测试了几个案例之后,我还发现你需要前向向量来完成这个计算。 所以下面没有进一步说明我正在使用的代码:

 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使用流行的Eigen库。 如果您不想使用它,那么您可能需要更换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), ux() * s, uy() * s, uz() * s); } 

请注意,点积0或1或-1的特殊情况会自动处理,因为normalized()为特征库中的零向量返回0。

在旁注中,对于您所有的转换担忧, 这是一个很棒的文档。