有多种方法可以做到这一点。您可以计算绝对方向或相对于化身的旋转,这意味着您的新方向= avatarOrientation * q。这是后者:
通过获取化身的单位前向矢量与从化身到目标的单位矢量(新的前向矢量)的叉积来计算旋转轴:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
使用点积计算旋转角度
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
使用rotAxis和rotAngle创建四元数,并将其与头像的当前方向相乘
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
如果您需要帮助来查找化身的当前前向矢量,则输入1.即可:)
编辑:计算绝对方向实际上要容易一些,使用恒等矩阵的正向向量代替化身正向向量作为1)和2)的输入。并且不要将其乘以3),而是直接将其用作新方向:newRot = q
重要说明:该解决方案有2个由叉积的性质引起的异常:
如果前向向量相等。解决方案这里只是返回标识四元数
如果向量恰好指向相反的方向。解决方案是通过使用化身上轴作为旋转轴以及角度为180.0度来创建四元数。
这是在C ++中实现的那些边缘情况的实现。将其转换为C#应该很容易。
// returns a quaternion that rotates vector a to vector b
quaternion get_rotation(const vector &a, const vector &b, const vector &up)
{
ASSERT_VECTOR_NORMALIZED(a);
ASSERT_VECTOR_NORMALIZED(b);
float dot = vector::dot(a, b);
// test for dot -1
if(nearly_equal_eps_f(dot, -1.0f, 0.000001f))
{
// vector a and b point exactly in the opposite direction,
// so it is a 180 degrees turn around the up-axis
return quaternion(up, gdeg2rad(180.0f));
}
// test for dot 1
else if(nearly_equal_eps_f(dot, 1.0f, 0.000001f))
{
// vector a and b point exactly in the same direction
// so we return the identity quaternion
return quaternion(0.0f, 0.0f, 0.0f, 1.0f);
}
float rotAngle = acos(dot);
vector rotAxis = vector::cross(a, b);
rotAxis = vector::normalize(rotAxis);
return quaternion(rotAxis, rotAngle);
}
编辑:马克的XNA代码的更正的版本
// the new forward vector, so the avatar faces the target
Vector3 newForward = Vector3.Normalize(Position - GameState.Avatar.Position);
// calc the rotation so the avatar faces the target
Rotation = Helpers.GetRotation(Vector3.Forward, newForward, Vector3.Up);
Cannon.Shoot(Position, Rotation, this);
public static Quaternion GetRotation(Vector3 source, Vector3 dest, Vector3 up)
{
float dot = Vector3.Dot(source, dest);
if (Math.Abs(dot - (-1.0f)) < 0.000001f)
{
// vector a and b point exactly in the opposite direction,
// so it is a 180 degrees turn around the up-axis
return new Quaternion(up, MathHelper.ToRadians(180.0f));
}
if (Math.Abs(dot - (1.0f)) < 0.000001f)
{
// vector a and b point exactly in the same direction
// so we return the identity quaternion
return Quaternion.Identity;
}
float rotAngle = (float)Math.Acos(dot);
Vector3 rotAxis = Vector3.Cross(source, dest);
rotAxis = Vector3.Normalize(rotAxis);
return Quaternion.CreateFromAxisAngle(rotAxis, rotAngle);
}
0*(rotation A) + 1*(rotation B)
-换句话说,您只是将旋转设置为旋转B很长一段时间。Slerp仅用于确定旋转方式应介于两者之间的方式(0%<x <100%)。