22

解决了


我正在我的引擎中制作一个 3D 门户系统(如 Portal 游戏)。每个门户都有自己的方向保存在一个四元数中。要在其中一个门户中渲染虚拟场景,我需要计算两个四元数之间的差异,并将结果用于旋转虚拟场景。

在左墙上创建第一个门户,在右墙上创建第二个时,从一个到另一个的旋转将仅在一个轴上发生,但例如当第一个门户将在地板上创建时,第二个在右墙上,从一个到另一个的旋转可能在两个轴上,这就是问题所在,因为旋转出错了。

我认为问题的存在是因为例如X轴和Z轴的方向一起存储在一个四元数中,我需要单独手动乘以X* Z(或Z* X),但是如何只使用一个四元数(差异四元数)?还是有其他方法来纠正旋转场景?

编辑:

这张图片上有两个门户 P1 和 P2,箭头显示它们是如何旋转的。当我查看 P1 时,我会看到 P2 中的内容。为了找到我需要旋转主场景的旋转,就像这张图片中的虚拟场景一样,我正在执行以下操作:

  1. 从四元数 P2 到四元数 P1
  2. Y 轴旋转 180 度的结果(传送门的 UP)
  3. 使用结果旋转虚拟场景

仅当差异仅发生在一个轴上时,上述方法才有效。当一个门户将在地板上或天花板上时,这将不起作用,因为差异四元数构建在多个轴上。正如建议的那样,我尝试将 P1 的四元数乘以 P2 的四元数,反之亦然,但这不起作用。

在此处输入图像描述

编辑2:

为了找到从 P2 到 P1 的区别,我正在执行以下操作:

Quat q1 = P1->getOrientation();
Quat q2 = P2->getOrientation();

Quat diff = Quat::diff(q2, q1);  // q2 * diff = q1 //

这是 Quat::diff 函数:

GE::Quat GE::Quat::diff(const Quat &a, const Quat &b)
{
    Quat inv = a;
    inv.inverse();
    return inv * b;
}

逆:

void GE::Quat::inverse()
{
    Quat q = (*this);
    q.conjugate();
    (*this) = q / Quat::dot((*this), (*this));
}

共轭:

void GE::Quat::conjugate()
{
    Quat q;
    q.x = -this->x;
    q.y = -this->y;
    q.z = -this->z;
    q.w = this->w;

    (*this) = q;
}

点积:

float GE::Quat::dot(const Quat &q1, const Quat &q2)
{
    return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
}

操作员*:

const GE::Quat GE::Quat::operator* ( const Quat &q) const
{
    Quat qu;
    qu.x = this->w*q.x + this->x*q.w + this->y*q.z - this->z*q.y;
    qu.y = this->w*q.y + this->y*q.w + this->z*q.x - this->x*q.z;
    qu.z = this->w*q.z + this->z*q.w + this->x*q.y - this->y*q.x;
    qu.w = this->w*q.w - this->x*q.x - this->y*q.y - this->z*q.z;
    return qu;
}

操作员/:

const GE::Quat GE::Quat::operator/ (float s) const
{
    Quat q = (*this);
    return Quat(q.x / s, q.y / s, q.z / s, q.w / s);
}

所有这些东西都有效,因为我已经使用GLM库对其进行了测试

4

5 回答 5

40

如果你想找到一个diff这样的四元数diff * q1 == q2,那么你需要使用乘法逆:

diff * q1 = q2  --->  diff = q2 * inverse(q1)

where:  inverse(q1) = conjugate(q1) / abs(q1)

and:  conjugate( quaternion(re, i, j, k) ) = quaternion(re, -i, -j, -k)

如果你的四元数是旋转四元数,它们都应该是单位四元数。这使得求逆很容易:因为abs(q1) = 1, 你inverse(q1) = conjugate(q1)可以通过取反ijk组件来找到。


但是,对于您描述的那种基于场景的几何配置,您可能实际上并不想执行上述操作,因为您还需要正确计算平移。

正确完成所有事情的最直接方法是将四元数转换为 4x4 旋转矩阵,并以适当的顺序将它们与 4x4 平移矩阵相乘,如大多数介绍性计算机图形学文本中所述。

当然可以手动组合欧几里得变换,保持四元数形式的旋转,同时将四元数增量应用于单独的平移向量。然而,这种方法在技术上往往晦涩难懂,并且容易出现编码错误:4x4 矩阵形式之所以是传统形式是有充分理由的,其中一个重要的原因是它似乎更容易以这种方式正确处理。

于 2014-03-04T08:57:02.663 回答
5

我解决了我的问题。事实证明,我不需要两次旋转之间的任何区别。只需将一个旋转乘以 180 度的旋转,然后以这种方式乘以第二个旋转的倒数(使用矩阵):

Matrix m1 = p1->getOrientation().toMatrix();
Matrix m2 = p2->getOrientation().toMatrix();
Matrix model = m1 * Matrix::rotation(180, Vector3(0,1,0)) * Matrix::inverse(m2);

并以这种方式计算翻译:

Vector3 position = -p2->getPosition();
position = model * position + p1->getPosition();
model = Matrix::translation(position) * model;
于 2014-03-04T12:53:00.420 回答
1

不,你必须将两个四元数相乘才能得到你想要的最后一个四元数。

假设您的第一个轮换是q1,第二个轮换是q2。您想按该顺序应用它们。

生成的四元数将是q2 * q1,这将代表您的复合旋转(回想一下,四元数使用左手乘法,因此通过从左侧乘法来q2应用)q1

参考

有关计算单个四元数的简要教程,请参阅我之前的堆栈溢出答案

编辑:

为了澄清,您将面临旋转矩阵和欧拉角的类似问题。您定义关于 X、Y 和 Z 的转换,然后将它们相乘以获得结果转换矩阵 ( wiki )。你在这里有同样的问题。旋转矩阵和四元数在大多数表示旋转的方式上是等价的。四元数是首选,主要是因为它们更容易表示(并且更容易解决万向节锁定)

于 2014-03-03T21:37:19.517 回答
1

四元数的工作方式如下:局部参考系表示为虚构的四元数方向 i、j、k。例如,对于站在门户门1中并沿箭头方向看的观察者,方向i可以表示箭头的方向,j向上并且k=ij指向观察者的右侧。在由四元数 q1 表示的全局坐标中,3D 坐标中的轴是

q1*(i,j,k)*q1^-1=q1*(i,j,k)*q1',

其中 q' 是共轭,对于单位四元数,共轭是逆。

现在的任务是找到一个单位四元数 q,使得在全局坐标中表示的局部坐标系 1 中的方向 q*(i,j,k)*q' 与全局坐标中坐标系 2 的旋转方向一致。从这意味着向前变成向后,向左变成向右的草图,即

q1*q*(i,j,k)*q'*q1'=q2*(-i,j,-k)*q2'
                   =q2*j*(i,j,k)*j'*q2'

这很容易通过等式来实现

q1*q=q2*j or q=q1'*q2*j.

但细节可能有所不同,主要是另一个轴可能代表“向上”方向而不是j。


如果草图的全局系统是从底部开始的,那么global-i在垂直方向上指向前方,global-j向上,global-k向右,那么local1-(i,j,k)就是global-( -i,j,-k),给出

q1=j. 

local2-(i,j,k) 是 global-(-k,j,i) 可以通过以下方式实现

q2=sqrt(0.5)*(1+j), 

自从

(1+j)*i*(1-j)=i*(1-j)^2=-2*i*j=-2*k and 
(1+j)*k*(1-j)=(1+j)^2*k= 2*j*k= 2*i

将其与实现中的实际值进行比较将表明必须如何更改轴和四元数方向的分配。

于 2014-03-03T23:18:50.093 回答
-2

检查https://www.emis.de/proceedings/Varna/vol1/GEOM09.pdf

想象一下从 Q1 到 Q2 得到 dQ,我会解释为什么 dQ = Q1*·Q2,而不是 Q2·Q1*

这会旋转框架,而不是对象。对于 R3 中的任意向量 v,算子 L(v) 的旋转动作 = Q*·v·Q

不是Q·v·Q*,是物体旋转动作。

如果先旋转 Q1,然后旋转 Q1*,然后旋转 Q2,则可以写为 (Q1·Q1*·Q2)*·v·(Q1·Q1*·Q2) = (Q1*·Q2)*·Q1*·v·Q1 ·(Q1*·Q2) = dQ*·Q1*·v·Q1·dQ

所以dQ = Q1*·Q2

于 2018-02-06T02:45:20.963 回答