两个四元数之间的差异

Tom*_*Tom 13 math portal-system rotation quaternions

解决了


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

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

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

编辑:

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

  1. 从四元数P2到四元数P1的区别
  2. 在Y轴上旋转180度(门户网站向上)
  3. 使用结果旋转虚拟场景

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

在此输入图像描述

编辑2:

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

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

Quat diff = Quat::diff(q2, q1);  // q2 * diff = q1 //
Run Code Online (Sandbox Code Playgroud)

这是Quat :: diff函数:

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

逆:

void GE::Quat::inverse()
{
    Quat q = (*this);
    q.conjugate();
    (*this) = q / Quat::dot((*this), (*this));
}
Run Code Online (Sandbox Code Playgroud)

共轭:

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

    (*this) = q;
}
Run Code Online (Sandbox Code Playgroud)

点产品:

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;
}
Run Code Online (Sandbox Code Playgroud)

运算符*:

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;
}
Run Code Online (Sandbox Code Playgroud)

运营商/:

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);
}
Run Code Online (Sandbox Code Playgroud)

所有这些都有效,因为我已经用GLM库测试了它

com*_*orm 25

如果你想找到一个四元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)
Run Code Online (Sandbox Code Playgroud)

如果四元数是旋转四元数,则它们都应为单位四元数.这使得简单的求逆:因为abs(q1) = 1,你inverse(q1) = conjugate(q1)可以通过只否定发现i,jk组件.


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

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

当然可以手工编写欧几里德变换,保持旋转以四元数形式,同时将四元数递增地应用于单独的平移向量.然而,这种方法在技术上往往模糊不清并且容易出现编码错误:4x4矩阵形式是常规的有很好的理由,而其中一个重要的原因是它似乎更容易以这种方式正确.

  • +1。四元数是 SO(3) 的双映射。特别地,q 和-q 表示相同的旋转。这意味着有一种更简单的方法来计算 q1*共轭 (q2):求反 q2 的实部而不是虚部,然后相乘。 (2认同)

Tom*_*Tom 6

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

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

翻译计算方式如下:

Vector3 position = -p2->getPosition();
position = model * position + p1->getPosition();
model = Matrix::translation(position) * model;
Run Code Online (Sandbox Code Playgroud)

  • 很高兴你解决了你的问题,汤姆。感谢您将其发布在这里,以便其他人将来可以看到它。 (3认同)