使用四元数:我该怎么办?(没有数学)


24

我是游戏开发人员,没有学习数学。所以我只想使用四元数作为工具。为了能够进行3D旋转,有必要使用四元数(或矩阵,但在本问题中,我们先保留四元数)。我认为对于许多开发人员来说,使用它们很重要。这就是为什么我想分享我的知识并希望填补我的空缺。现在....

据我了解:

四元数可以描述两件事:

  1. 3d对象的当前方向。
  2. 对象可以执行的旋转变换。(rotationChange)

您可以使用四元数:

乘法:

  1. 四元数endOrientation =四元数旋转Change *四元数currentOrientation;

    因此,例如:我的3D对象向左旋转90°-我乘的旋转就是向右旋转180°,最后我的3D对象90°向右旋转。

  2. 四元数rotationChange =四元数endRotation * Quaternion.Inverse(startRotation);

    这样,您将获得一个rotationChange,可以将其应用于另一个方向。

  3. Vector3 endPostion =四元数rotationChange * Vector3 currentPosition;

    因此,例如:我的3D对象位于位置(0,0,0)上,并且我乘以的旋转是向右旋转180°,我的端点类似于(0,-50,0)。在四元数内部有一个轴-以及围绕该轴的旋转。您围绕该轴Y度旋转点。

  4. Vector3 rotationOffsetVector =四元数rotationChange * Vector3 currentOffsetVector;

    例如:我的开始方向显示为UP-(0,1,0),我乘以的旋转方向是向右旋转180°,我的结束方向显示为向下。(0,-1,0)

混合(Lerp和Slerp):

  1. 四元数currentOrientation =四元数.Slerp(startOrientation,endOrientation,插值器)

    如果插值器为1:currentOrientation = endOrientation

    如果插值器为0:currentOrientation = startOrientation

    Slerp插值更精确,而Lerp插值更高效。

我的问题:

我到目前为止所解释的一切都正确吗?

四元数可以做到这一点吗?(不可以)

您还能对他们做什么?

两个四元数之间的点积和叉积有什么好处?

编辑:

更新了问题并提供了一些答案


假设您没有2,但是n方位(姿势,姿势等)不同。然后,您可以使用权重对它们进行平均,从而有效地概括slerp / lerp。您还可以将四元数转换为转子,这等效于对刚体施加一定时间的角速度。因此,您也可以描述四元数的角速度积分。您还可以估计两个方向的相异程度(计算超球面上两个四元数所跨越的弧的长度)。
teodron '16

是的,乍一看,您的基本原理是正确的(您对四元数的理解对于非技术人员来说是非常好的)。这不适合发表评论,但是恭喜!尽管四元数的用途只是作为软件工程工具的目的,但即使是技术天赋的人也不知道所有四元数的用途。
teodron '16

4
“并且要能够进行3D旋转,必须使用四元数。”我无法强调这句话有多假。您可以使用Euler角或Tait-Bryan角进行游戏开发,唯一的问题是云台锁定。如果您想成为游戏开发人员,则需要同时掌握数学并进行学习。
巴林特

1
“游戏开发者”和“不学习数学”是矛盾的。
玛格丽特·布鲁姆

2
感谢您尝试解决此问题,但是答案应该在答案中,而不是问题中。如果您认为值得整理一下,请做一个“摘要”答案。
基本

Answers:


23

乘法

至少就Unity对四元数的实现而言,问题中描述的乘法顺序是不正确的。这很重要,因为3D旋转不是可交换的

因此,如果我想rotationChange从对象开始旋转对象,currentOrientation可以这样写:

Quaternion newOrientation = rotationChange * currentOrientation;

(即,转换向左堆叠-与Unity的矩阵约定相同。最右边的旋转首先应用/在“最本地”的一端)

如果我想通过旋转来变换方向或偏移矢量,可以这样写:

Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;

(如果相反,Unity将生成一个编译错误)

调和

在大多数情况下,您可以摆脱Lerping旋转。这是因为在四元数中“引擎盖下”使用的角度是旋转角度的一半,因此它比Matrix之类的东西(通常不会使 Lerp更好!)更接近Lerp的线性近似。请观看此视频约40分钟,以获取更多说明

真正需要Slerp的一种情况是,您需要随着时间的推移保持一致的速度,例如在动画时间轴上的关键帧之间进行插值。对于只关心输出介于两个输入之间的情况(例如动画的混合层),通常Lerp效果很好。

还有什么?

两个单位四元数的点积给出了它们之间夹角的余弦值,因此,如果需要比较旋转,则可以使用点积作为相似性度量。但是,这有点晦涩难懂,因此对于更清晰易懂的代码,我经常使用Quaternion.Angle(a,b)代替,它更清楚地表示我们正在以熟悉的单位(度)比较角度。

Unity为四元数提供的这些便利方法类型非常有用。在几乎每个项目中,我至少都会使用一次该项目

Quaternion.LookRotation(Vector3 forward, Vector3 up)

这将生成一个四元数:

  • 旋转局部z +轴以精确地指向forwardvector参数
  • 旋转局部y +轴,使其指向尽可能靠近upvector参数的位置(如果提供),或者指向,(0, 1, 0)如果省略

“向上”仅得到“尽可能接近”的原因是系统超定。面向z +会forward消耗两个自由度(即偏航和俯仰),因此我们只剩下一个自由度(侧倾)。

我经常发现我想要一些具有相反的精确度属性的东西:我希望局部y +精确地沿指向up,而局部z +尽可能forward与剩余自由度保持接近。

例如,当尝试为运动输入形成相对于相机的坐标系时,就会出现这种情况:我希望我的局部向上方向保持垂直于地板或倾斜表面法线,因此我的输入不会尝试将角色引导到地形中或让他们振作起来。

如果您希望坦克的炮塔外壳面向目标,而在向上/向下瞄准时不会从坦克的主体上剥落,您也可能会遇到这种情况。

为此,我们可以构建自己的便捷功能,以LookRotation进行繁重的工作:

Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
    Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
    Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);

    return rotateZToUp * rotateYToZ;
}

在这里,我们首先将局部y +旋转到z +,然后将局部z +旋转到y-。

然后,我们将新的z +向上旋转(因此,净结果是直接沿局部y +点exactUp),新的y +尽可能靠近正向否定的方向(因此,净结果是沿z的局部点尽可能靠近)approximateForward

另一个方便的便捷方法是Quaternion.RotateTowards,我经常这样使用:

Quaternion newRotation = Quaternion.RotateTowards(
                             oldRotation, 
                             targetRotation,
                             maxDegreesPerSecond * Time.deltaTime
                         );

这样targetRotation一来,无论帧速率如何,我们都可以以一致且可控的速度收尾-对于影响游戏机制(例如转动角色的动作或对玩家进行炮塔跟踪)的结果/公平性的旋转非常重要。在这种情况下,天真地捆扎/绑扎很容易导致机芯在高帧率下变得响亮的情况,从而影响游戏平衡。(这并不是说这些方法是错误的-有在不改变公平性的情况下正确使用它们的方法,只需要小心即可。RotateTowards提供了一个方便的快捷方式来为我们解决这个问题)


提示:在视频URL的末尾添加&t = 40m,这样它将直接跳到该位置(可选,例如40m5s)。四元数点乘积在处理球形游戏世界时也很方便-或在定向旋转球体的块时更广泛地使用。
卢克·布里格斯

@Luke Briggs:如果您愿意的话,那么球形的游戏世界点听起来很值得在它自己的答案中进行阐述(尤其是使用图表)。:)
DMGregory

好主意-现在是凌晨3点(所以我想这会有点乱了!),但我很高兴明天将一些东西整理在一起(如果我还记得的话)
Luke Briggs

1
编辑:我有点想起一个答案,所以接受挑战!我至少将其标记为粗切,这样人们才能意识到它所造成的深夜烧伤:P
Luke Briggs

好了!我试图以图形化的概述来介绍它,因为您的答案已经很好地涵盖了基础功能。我想该睡觉了!
卢克·布里格斯

14

点产品在哪里使用?

在Unity中,点乘积的最普通用户之一是每当您通过==或检查两个四元数是否相等时!=。Unity计算点积以检查相似性,而不是直接比较内部x,y,z,w值。记住这一点是值得的,因为它会使通话比您预期的昂贵。

我们也在一个有趣的用例中使用它。

四元数点积的乐趣-球形世界和轨道

整个行星甚至整个太阳系的仿真越来越普遍。为了实时实现这一点,我们也需要四元数点积。其中很多。四元数点产品的使用率很低,但肯定有其用途-让我们来看一下!

首先,我们要考虑一系列轮换:

  1. (可选)银河系中心周围的恒星
  2. 围绕恒星的行星
  3. 行星的倾斜
  4. 行星的自旋
  5. 附近的网格单元的位置(绕行星核心旋转)*
  6. 多个轨道平面

将它们全部组合在一起,最终会带来很多复杂性(并且数量巨大!)。当观看者站在行星表面上时,我们不希望他们在我们的游戏世界中以某种疯狂的速度受伤。实际上,我们希望它们静止不动并位于原点附近—而是将宇宙移动到玩家周围。

旋转行星

重要的是,为了在这种情况下能够正确校正行星的旋转和倾斜,我们需要轴向锁定杆,以便杆只能在上面的图像上向上/向下摆动(即,玩家移动时“向上”摆动)北)。这就是四元数点积的来源。如果我们在这里不使用点积,而只是乘以倾斜度,就会发生这种情况:

错误倾斜的“行星”

请注意,我们绕行轨道的“行星”的两极始终向着恒星倾斜。事实并非如此-倾斜方向固定的

在不离题不远的情况下,以下是快速摘要:

  • 在球体上,方向还可以整齐地描述表面位置。
  • 我们有很多旋转可以组合在一起。
  • 将一切描述为轮换;观众的位置。由于最终我们减少了操作,因此有助于提高性能。
  • 旋转之间的角度(我们的点积)有助于测量经度,并且在处理倾斜时效果特别好。

通过仅获得角度,我们可以减少一些不必要的旋转。同时,我们还进行了经度测量,这对于导航以及当地气候非常有用。

*行星由许多网格单元构成。实际上只有附近的显示。


2
这在设置场景和激发问题方面做得很好,但是我对四元数点乘积(即标量乘积dot(a, b) = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w而不是我们用来链接在一起的四元数组成)的数学仍然有点模糊轮换)将有助于我们解决此问题。如果您以后能详细讲解,我会很乐意投票(我并不是要让您摆脱束缚……我是要睡觉!)
DMGregory

@DmGregory的简短答案是倾斜是单数;除了那一个之外,其他所有事物都很好地组成(否则,行星似乎会围绕其恒星摆动)。我(希望!)明天再添加一些上下文!
路加·布里格斯

@DMGregory我添加了一些其他信息(我无法入睡!)-希望可以使其更清楚。
卢克·布里格斯

1
抱歉,如果我有点忙,但是在重新阅读了几次之后,我仍然不知道如何在公式中使用点积来实现您要描述的转换。您是否可以添加一些伪代码来布局您明确执行的操作?
DMGregory

@DMGregory我对四元数不熟悉,但是如果这是球体上的旋转,则这不是旋转合成。这是使用带有矢量的球形几何图形来计算任意圆周的AKA球面上“线”的法线矢量。再次,这个答案也没有意义,但我相信他们使用的是球形几何。
大鸭
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.