7

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

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

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
4

3 回答 3

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)

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

诺埃尔休斯 nhughes1ster@gmail.com

于 2009-10-27T04:07:25.830 回答
0

这是一个更符合标题的解决方案:“来自正交基的四元数”

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

通过将 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)

于 2021-10-31T08:24:53.723 回答
-1

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

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

于 2009-10-25T08:41:02.830 回答