我正在作为学校项目在XNA上开发3D太空射击手(基本上是3D小行星,带有加电功能),并且一直在努力实现相对于船舶局部轴的滚动,俯仰和偏航。 (我应该强调:旋转不是相对于绝对/世界x,y和z轴的。)可悲的是,最近几周我一直在为此而苦苦挣扎。 Google和我的新石器时代的猴子脑使我失望。也许你们可以帮忙!

这是我的设置:

通过键盘输入,我可以准备以下变量:


yawRadians,将所需的偏航存储在远离船首的地方
位置
pitchRadians,它存储了远离
船的初始位置
rollRadians,用于存储所需的滚动
离船的初始位置


该船还保持自己的前,后,右,左,上和下单位矢量,这些矢量既用于旋转也用于推进。 (不同的按键将推动船舶驶向前,后等方向。这部分效果很好。)

最终,我生成了表示所有船舶旋转的旋转矩阵mShipRotation,该矩阵传递给船舶的draw方法。

我的问题是轮换本身。我尝试过的不同解决方案产生了不同的结果。到目前为止,这是我去过的地方:

方法1 –相对于绝对/世界x,y和z轴的偏航,俯仰和横滚

一开始,我天真的尝试在飞船的Update方法中使用以下命令:

qYawPitchRoll = Quaternion.CreateFromYawPitchRoll(yawRadians, pitchRadians, rollRadians);

vFront = Vector3.Transform(vOriginalFront, qYawPitchRoll);
vBack = -1 * vFront;

vRight = Vector3.Transform(vOriginalRight, qYawPitchRoll);
vLeft = -1 * vRight;

vTop = Vector3.Transform(vOriginalTop, qYawPitchRoll);
vBottom = -1 * vTop;

mShipRotation = Matrix.CreateFromQuaternion(qYawPitchRoll);


(vOriginalFront,vOriginalRight和vOriginalTop仅存储船的初始方向。)

除了旋转始终相对于x,y和z轴,而不是相对于船舶的前/后/右/左/左/上/下矢量,上述操作实际上没有任何错误。这导致船不总是偏航和俯仰。 (特别是,如果您已俯仰,则偏航会退化为滚动,这样船就指向顶部。这是有道理的,因为此解决方案中的偏航只是绕着世界上的轴旋转。)

我听说过Quarternion.CreateFromAxisAngle方法,听起来很完美。我可以将三个四元数旋转组合起来,围绕船的每个局部轴旋转一个。可能出什么问题了?

方法2 – Quaternion.CreateFromAxisAngle

这是我在飞船的Update方法中使用的第二个代码段:

qPitch = Quaternion.CreateFromAxisAngle(vRight, pitchRadians);
qYaw = Quaternion.CreateFromAxisAngle(vTop, yawRadians);
qRoll = Quaternion.CreateFromAxisAngle(vFront, rollRadians);
qPitchYawAndRoll = Quaternion.Concatenate(Quaternion.Concatenate(qPitch, qYaw), qRoll);

vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, qPitchYawAndRoll));
vBack = -1 * vFront;
vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, qPitchYawAndRoll));
vLeft = -1 * vRight;

vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, qPitchYawAndRoll));
vBottom = -1 * vTop;

mShipRotation = Matrix.CreateFromQuaternion(qPitchYawAndRoll);


如果我一次只旋转一圈(偏航,俯仰或翻滚),上述方法就可以很好地发挥作用,但是如果同时进行多于一圈的旋转,船将开始疯狂旋转并指向许多不同的方向,从而变得越来越弯曲直到它完全消失。

我尝试了上面的变体,首先将变桨应用于所有矢量,然后是偏航,然后是滚转,但是没有运气。

尽管有人担心Gimbal Lock,但我也直接使用矩阵进行了尝试:

方法3:矩阵

mShipRotation = Matrix.Identity;

mShipRotation *= Matrix.CreateFromAxisAngle(vRight, pitchRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vFront, rollRadians);
mShipRotation *= Matrix.CreateFromAxisAngle(vTop, yawRadians);

vFront = Vector3.Normalize(Vector3.Transform(vOriginalFront, mShipRotation));
vBack = -1 * vFront;

vRight = Vector3.Normalize(Vector3.Transform(vOriginalRight, mShipRotation));
vLeft = -1 * vRight;

vTop = Vector3.Normalize(Vector3.Transform(vOriginalTop, mShipRotation));
vBottom = -1 * vTop;


没运气;我有同样的行为。一次可以旋转一圈,但是绕多个轴旋转会导致相同的异常旋转行为。

经过一些出色的调试(读作:盲目地将变量输出到控制台)之后,我注意到Front / Right / Top向量随着时间的推移逐渐变慢,彼此之间的正交性降低了。我基本上在向量的每个步骤中都向向量添加了归一化,并且还尝试基于叉积计算新向量,以确保它们始终彼此垂直,但即使那样它们也不是完全正交的。我猜这是由于浮点数学不够精确。

请注意,我会通过每个Update方法重新生成mShipRotation矩阵,因此它不能直接累积漂移或不准确性。我认为,应用多个Quarternion旋转可能会累积误差(因为我可以做一次旋转就很好了),但是我修复它的尝试没有用。

简而言之:


我可以相对于世界坐标轴x,y和z进行俯仰/滚动/偏航
精细。这并不是玩家期望发生的
滚动/俯仰/偏航与船舶无关,而是与
世界。
我可以绕着船只的局部轴(前/后/上/下/左/右)滚动,俯仰或偏航,但一次只能一次。它们的任何组合都将导致船舶迅速旋转并变形。


我已经尝试过四元数和矩阵。我尝试了在各种论坛中找到的建议,但最终没有找到可行的解决方案。通常,人们建议使用Quaternion.CreateFromYawPitchRoll,而不是真正意识到其意图是使飞船绕其自身的(不断变化的)轴而不是(固定的)世界轴旋转。

有任何想法吗?在给定情况的情况下,您将获得关于船舶的前,右和上矢量的侧倾,俯仰和偏航,您将如何创建旋转矩阵?

最佳答案

您似乎在方法2和方法3中将总角度(yawRadians,pitchRadians,rollRadians)应用到局部轴。这些值与世界轴结合在一起,在局部空间中没有任何意义。问题的根源在于要挂在这三个角度上。

在局部空间中,使用一个角度量,该角度量是您要在帧之间旋转的量。如果自上一帧以来仅倾斜0.002f弧度,那将是绕vRight轴旋转时要使用的弧度。

这将扭曲您的总体角度值(yawRadians,pitchRadians和rollRadians),并使它们失去作用,但大多数坚持3d编程的人会迅速放弃角度方法来存储方向。

只需围绕着局部轴一点一点地旋转矩阵或四元数,然后将方向(而不是3个角度)存储在该结构中(四度或矩阵)即可。

像这样绕着局部轴旋转矩阵时,无需担心万向架锁定问题。您必须在帧之间进行90度旋转才能将其带入图片。

如果要避免错误累积,请使用quat来存储方向并在每个帧中对其进行规范化。然后,将发送到效果的矩阵从quat的每一帧生成,并且将是正交的。即使您没有使用quat并将方向存储在矩阵中,也要花费数小时或数天的时间才能累积足够的误差以使视觉上可以察觉。

该博客可能会有所帮助:http://stevehazen.wordpress.com/2010/02/15/matrix-basics-how-to-step-away-from-storing-an-orientation-as-3-angles/

07-26 05:45
查看更多