c# - XNA - 关于本地(改变)船轴的 3D 旋转 - 我错过了什么?

标签 c# 3d xna

我正在 XNA 中开发一个 3D 太空射击游戏作为学校项目(基本上是带有电源的 3D 小行星),并且一直致力于实现关于船舶局部轴的滚动、俯仰和偏航。 (我应该强调:旋转与绝对/世界 x、y 和 z 轴无关。)可悲的是,过去几周我一直在努力解决这个问题。谷歌和我的新石器时代猴脑让我失望;也许你们能帮上忙!

这是我的设置:

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

  • 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);
    

    如果我一次只做一个旋转(偏航、俯仰或滚动),上面的方法很完美,但是如果我同时组合不止一个旋转,船就会开始疯狂地旋转并指向许多不同的方向,变得越来越扭曲直到它完全消失。

    我已经尝试了上述变体,我首先将 Pitch 应用于所有向量,然后是 Yaw,然后是 Roll,但没有运气。

    尽管担心 Gimbal Lock,我也直接使用 Matrices 进行了尝试:

    方法 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 向量随着时间的推移慢慢变得彼此不正交。我基本上在每一步都为向量添加了归一化,并尝试基于叉积计算新向量,以确保它们始终保持彼此垂直,但即便如此,它们也不是完全正交的。我猜这是由于浮点数学不是非常精确。

    请注意,我每次更新方法都会重新生成 mShipRotation 矩阵,因此它不能直接累积漂移或不准确度。我认为应用多个四元数旋转可能会累积错误(因为我可以做一次旋转就好了),但我尝试修复它并没有奏效。

    简而言之:
  • 我可以相对于世界轴 x、y 和 z 进行俯仰/滚动/偏航
    美好的。这不是玩家期望发生的事情
    滚动/俯仰/偏航不是相对于船,而是相对于
    世界。
  • 我可以很好地围绕船舶的局部轴(前/后/上/下/左/右)滚动、俯仰或偏航,但一次只能做一个。它们的任何组合都会导致船快速旋转和变形。

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

    有任何想法吗?假设您获得了关于船的前部、右部和顶部向量的滚转、俯仰和偏航,您将如何创建旋转矩阵?

    最佳答案

    您似乎在方法 2 和 3 中将整体角度(yawRadians、pitchRadians、rollRadians)应用于局部轴。这些值与世界轴结合,在局部空间中没有意义。你的问题的根源是想卡在 3 个角度上。

    在局部空间中,使用的角度量是您要在帧之间旋转的量。如果自上一帧以来仅向上倾斜 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/

    关于c# - XNA - 关于本地(改变)船轴的 3D 旋转 - 我错过了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8292398/

    相关文章:

    c# - asp.net c# 防止在从服务器端代码更改索引时触发 selectedindexchanged 事件

    c++ - 使用四元数方向的第一人称相机中的夹紧间距

    c# - Repeater、Custom Paging、PagedDataSource,可能吗?

    c# - 异步方法正在阻塞它正在执行的 UI 线程

    matlab - 在 MATLAB/Octave 中绘制 3D 矩阵切片的值

    winforms - 在哪里可以找到带有WinForms的XNA的代码示例?

    c# - 用 C# 编写脚本 - XNA 和 360 的可移植库?

    c# - 将 dll 和内容嵌入到 monogame 的 exe 中

    c# - 如何使用带字典的定时器

    java - TransformGroup 的正确用法