Math 两个四元数之差 解决了的

Math 两个四元数之差 解决了的,math,rotation,quaternions,portal-system,Math,Rotation,Quaternions,Portal System,我正在我的引擎中制作一个3D门户系统(比如门户游戏)。每个入口都有自己的方向,保存在四元数中。要在其中一个入口中渲染虚拟场景,我需要计算两个四元数之间的差,结果用于旋转虚拟场景 在左墙上创建第一个入口,在右墙上创建第二个入口时,从一个入口到另一个入口的旋转将仅在一个轴上进行,但例如,在地板上创建第一个入口,在右墙上创建第二个入口时,从一个入口到另一个入口的旋转可能在两个轴上,这就是问题所在,因为旋转出错了 我认为这个问题是存在的,因为方向(例如X轴和Z轴)存储在一个四元数中,我需要它分别手动相乘

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

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

我认为这个问题是存在的,因为方向(例如
X
轴和
Z
轴)存储在一个四元数中,我需要它分别手动相乘
X
*
Z
(或
Z
*
X
),但如何只使用一个四元数(差分四元数)?或者是否有其他方法可以正确旋转场景

编辑:

这张图片上有两个入口P1和P2,箭头显示了它们是如何旋转的。当我观察P1时,我将看到P2。要找到旋转,我需要旋转主场景,使其与此图片中的虚拟场景相似,我将执行以下操作:

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

Quat diff = Quat::diff(q2, q1);  // q2 * diff = q1 //
  • 获取从四元数P2到四元数P1的差
  • Y轴旋转180度的结果(入口向上)
  • 使用结果旋转虚拟场景
  • 上述方法仅适用于仅在一个轴上发生差异的情况。当一个入口位于地板或天花板上时,这将不起作用,因为差分四元数构建在多个轴上。正如我所建议的,我试着将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;
    }
    
    Dot产品:

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

    所有这些东西都是有效的,因为我已经用库对它进行了测试。不,你必须将两个四元数相乘才能得到你想要的最终四元数

    假设您的第一个旋转是
    q1
    ,第二个旋转是
    q2
    。您希望按该顺序应用它们

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

    有关计算单个四元数的简短教程,请参阅我的

    编辑:
    为了澄清这一点,您将面临旋转矩阵和欧拉角的类似问题。定义关于X、Y和Z的变换,然后将它们相乘,得到结果变换矩阵()。你也有同样的问题。旋转矩阵和四元数在大多数表示旋转的方法中是等价的。首选四元数主要是因为它们更容易表示(也更容易寻址万向节锁)

    四元数的工作方式如下:局部参考系表示为假想的四元数方向i、j、k。例如,对于站在入口门1中并朝箭头方向看的观察者,方向i可能代表箭头方向,j向上,k=ij指向观察者的右侧。在四元数q1表示的全局坐标中,三维坐标中的轴是

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

    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
    

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

    如果要查找四元数
    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
    ,所以只要对
    i
    j
    k
    分量求反,就可以找到你的
    逆(q1)=共轭(q1)


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

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

    当然,手动合成欧几里德变换是可能的,保持旋转为四元数形式,同时将四元数增量应用于单独的平移向量。然而,这种方法在技术上往往是模糊的,并且容易出现编码错误:4x4矩阵形式是传统的有很好的理由,其中一个重要的理由是这样做似乎更容易正确
    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;