正交基础的四元数

问题描述 投票:0回答:4

我有一个沿着速度矢量移动的射弹物体。我需要确保物体始终面向速度矢量的方向。此外,我使用四元数而不是矩阵来表示对象旋转。

我知道第一步是找到正交基:

forward = direction of velocity vector
up = vector.new(0, 1, 0)
right = cross(up, forward) 
up = cross(forward, right)

如何将基础转换为旋转四元数?

解决方案

注意,我想感谢 Noel Hughes 提供了答案,但我想用我自己的经历来澄清。伪代码如下:

   vec3 vel = direction of velocity vector
   vec3 forward = (1, 0, 0)  // Depends on direction your model faces. See below.
   vec3 axis = cross(forward, vel)
   if (axis == 0) then quit // Already facing the right direction!
   axis = normalize(axis)
   float theta = acos(vel.x/sqrt(vel.x^2, vel.y^2, vel.z^2))
   quat result = (0, axis.y * sin(theta/2), axis.z * sin(theta/2), cos(theta/2)

四元数的最后一个元素是标量部分,前三个元素是虚部。另外,上面的伪代码假设“模型空间”中的对象指向正 x 轴。就我而言,该对象实际上指向正 y 轴,在这种情况下,我进行了以下更改:

   vec3 vel = direction of velocity vector
   vec3 forward = (0, 1, 0)  // Note that y-component is now 1
   vec3 axis = cross(forward, vel)
   if (axis == 0) then quit 
   axis = normalize(axis)
   float theta = acos(vel.x/sqrt(vel.x^2, vel.y^2, vel.z^2))
   quat result = (axis.x * sin(theta/2), 0, axis.z * sin(theta/2), cos(theta/2)
   // Note that SECOND component above is now 0
game-physics quaternions
4个回答
4
投票

我假设你不关心射弹的方向,除了纵轴与速度矢量对齐,并且纵轴是 (1, 0, 0) 的 x 轴。

您走在正确的道路上。归一化速度向量,(vx, vy, vz)/sqrt(vx^2 + vy^2 + vz^2) 与其穿过x轴并归一化结果 - (0, yn, zn) - 这就是旋转四元数的轴。旋转角度就是 theta = vx/sqrt(vx^2 + vy^2 + vz^2) 的反余弦。所得四元数为

(0, yn, zn)sn(theta/2) cos(theta/2)

如果您有任何疑问,请告诉我。

诺埃尔·休斯 [电子邮件受保护]


1
投票

这里有一个更符合标题的解决方案: “正交基础的四元数”

上述问题和答案解决了沿向量对齐四元数(“指向”)但忽略扭曲(方向)的问题。完整的正交基包括方向、上和侧(其中一个是多余的,但不是两个)。原题只考虑方向。

通过将 3x 向量放入矩阵的列,然后将矩阵转换为四元数,可以找到基于正交的四元数的一种解决方案。然而,这种方法除了四元数之外还需要额外的矩阵存储(16x 浮点数),并且速度相当慢。

通过写出基向量 a、b、c 并将它们插入矩阵到四元数的转换中,可以找到更好的解决方案,其中元素是显式的。 (该矩阵是列主矩阵,OpenGL 形式)

m[0][0] = a.x, m[1][0] = a.y, m[2][0] = a.z
m[0][1] = b.x, m[1][1] = b.y, m[2][1] = b.z
m[0][2] = c.x, m[1][2] = c.z, m[2][2] = c.z

现在,我们使用替换的正交向量 a、b、c 重写矩阵到四元数的函数。结果是以下函数:

// Quaternion from orthogonal basis
Quaternion& Quaternion::toBasis (Vector3DF a, Vector3DF b, Vector3DF c)
{
    float T = a.x + b.y + c.z;
    float s;
    if (T > 0) {
        float s = sqrt(T + 1) * 2.f;
        X = (b.z - c.y) / s;
        Y = (c.x - a.z) / s;
        Z = (a.y - b.x) / s;
        W = 0.25f * s;
    } else if ( a.x > b.y && a.x > c.z) {
        s = sqrt(1 + a.x - b.y - c.z) * 2;
        X = 0.25f * s;
        Y = (a.y + b.x) / s;
        Z = (c.x + a.z) / s;
        W = (b.z - c.y) / s;
    } else if (b.y > c.z) {
        s = sqrt(1 + b.y - a.x - c.z) * 2;
        X = (a.y + b.x) / s;
        Y = 0.25f * s;
        Z = (b.z + c.y) / s;
        W = (c.x - a.z) / s;
    } else {
        s = sqrt(1 + c.z - a.x - b.y) * 2;
        X = (c.x + a.z) / s;
        Y = (b.z + c.y) / s;
        Z = 0.25f * s;
        W = (a.y - b.x) / s;
    }
    normalize();
    return *this;
}

该函数直接从正交基向量 a,b,c 给出四元数 (X,Y,Z,W),无需中间存储矩阵。 (上面的矩阵采用列主、OpenGL 形式)。您可能仍然需要标准化四元数。如果你有一个方向和向上向量,比如相机,你可以将基础构造为:a=dir, b=up, c=cross(dir,up)


0
投票

如果您使用 Unity(或提供此功能的类似库),您需要的函数是 Quaternion.LookRotation。只需传入你想要使用的前向和向上向量,它就会返回相应的四元数。


-1
投票

我想看看 vecmath 库(Java)。它已经存在很长时间了,我们在我们的社区中使用它。它基于 4 元组,如果没有简单的转换方法,我会感到失望。

我还会为预期结果编写单元测试。人们很容易混淆正负、左手和右手以及移动/参考系。从简单的问题(例如 xyz)开始,以确保您得到正确的答案。

© www.soinside.com 2019 - 2024. All rights reserved.