正交基的四元数

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

请参考此答案,该答案考虑了世界上的轴,以防止四元数滚动。 - Phrogz
3个回答

4

我假设您在意的只是把弹丸的纵向轴与速度矢量对齐,而不关心其方向,而纵向轴是 (1, 0, 0)。

您走在了正确的路上。将速度矢量 (vx, vy, vz) 规范化为 (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)

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

Noel Hughes nhughes1ster@gmail.com


那么,我可以使用(0,yn,zn)作为旋转轴,theta作为旋转角度来构建四元数吗?我有一种用这种方式创建四元数的方法:qx = ax * sin(angle/2) qy = ay * sin(angle/2) qz = az * sin(angle/2) qw = cos(angle/2) - Matt Fichman
1
我希望这个答案不要忽略弹丸的“方向”(滚动),也不要忽略“上”轴。这个答案对于弹丸来说还可以,但对于相机来说不好! - Phrogz

1
这里有一个更符合题目的解决方案:"基于正交基向量的四元数"
以上的问题和答案解决了将四元数与向量对齐(“指向”),但忽略了扭曲(方向)。完整的规范正交基包括方向、上下和侧面(其中一个是多余的,但不能是两个)。原始问题只考虑方向。
一种从正交基向量生成四元数的解决方案是将3x向量放入矩阵的列中,然后将矩阵转换为四元数。然而,这种方法需要额外的矩阵存储(16个浮点数)以及相当缓慢。
通过编写基向量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)


-1

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

我还建议编写预期结果的单元测试。很容易混淆正负、左右手和移动/参考框架。从简单的(例如xyz)开始,确保你得到了正确的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接