两个四元数之间的差异

时间:2014-03-03 21:06:49

标签: math rotation quaternions portal-system

解决


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

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

我认为问题的存在是因为示例X轴和Z轴的方向一起存储在一个四元数中,我需要单独地将其加到X * {{1} }(或Z * Z),但如何仅使用一个四元数(差异四元数)?或者还有其他方法来纠正旋转场景吗?

修改

此图片中有两个门户P1和P2,箭头显示它们是如何旋转的。在我看P1时,我会看到P2的内容。要找到我需要旋转主场景的旋转,就像这张图片中的虚拟场景一样,我正在做以下事情:

  1. 从四元数P2到四元数P1的差异
  2. 在Y轴上旋转结果180度(门户网站向上)
  3. 使用结果旋转虚拟场景
  4. 上述方法仅在差异仅在一个轴上发生时才有效。当一个门户在地板上或在天花板上时,这将不起作用,因为差异四元数是在多个轴上构建的。正如所建议的那样,我试图将P1的四元数乘以P2的四元数,反过来但这不起作用。

    enter image description here

    编辑2:

    要找到从P2到P1的差异我正在做以下事情:

    X

    这是Quat :: diff函数:

    Quat q1 = P1->getOrientation();
    Quat q2 = P2->getOrientation();
    
    Quat diff = Quat::diff(q2, q1);  // q2 * diff = q1 //
    

    逆:

    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;
    }
    

    所有这些都有效,因为我已使用GLM

    对其进行了测试

5 个答案:

答案 0 :(得分:16)

如果要查找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)ij组件即可找到k


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

正确执行所有操作的最简单方法是将四元数转换为4x4旋转矩阵,并按照适当的顺序将它们与4x4平移矩阵相乘,如大多数介绍性计算机图形文本中所述。

当然可以手动组合欧几里德变换,在将四元数逐步应用于单独的平移向量时保持四元数形式的旋转。然而,这种方法在技术上往往模糊不清,容易出现编码错误:有充分的理由说明4x4矩阵形式是常规的,其中一个重要的原因是它似乎更容易以这种方式正确。

答案 1 :(得分:3)

我解决了我的问题。事实证明,我不需要在两次旋转之间有任何区别。只需将一个旋转乘以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;

答案 2 :(得分:1)

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

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

生成的四元数将为q2 * q1,它将代表您的复合旋转(回想一下,四元数使用左侧乘法,因此q2通过左侧乘以q1来应用{{1}} )

Reference

有关计算单个四元数的简要教程,请参阅我的previous stack overflow answer

编辑:

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

答案 3 :(得分: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 up和global-k指向右边,那么local1-(i,j,k)是全局 - ( - i,j,-k),给予

q1=j. 

local2-(i,j,k)是全局的 - ( - 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

将其与实现中的实际值进行比较,将指示如何更改轴和四元数方向的分配。

答案 4 :(得分:-1)

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

想象一下从第一季度到第二季度的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