【问题标题】:How to properly rotate a quaternion along all axis?如何沿所有轴正确旋转四元数?
【发布时间】:2023-04-01 20:07:01
【问题描述】:

我想编写一个第一人称相机,并将其旋转存储在一个四元数中。不幸的是,轮换出了点问题。

以下函数负责旋转相机。参数MouseSpeed 传递鼠标移动和旋转速度。然后该函数获取旋转四元数,旋转它并存储结果。顺便说一句,我使用的是Bullet Physics,这是类型和函数的来源。

void Rotate(vec2 Mouse, float Speed)
{
    btTransform transform = camera->getWorldTransform();
    btQuaternion rotation = transform.getRotation();

    Mouse = Mouse * Speed;                    // apply mouse sensitivity
    btQuaternion change(Mouse.y, Mouse.x, 0); // create quaternion from angles
    rotation = change * rotation;             // rotate camera by that

    transform.setRotation(rotation);
    camera->setWorldTransform(transform);
}

为了说明鼠标移动时产生的相机旋转,我向您展示了一幅手绘图。左侧显示了相机实际执行的错误旋转。右侧显示了所需的正确大小写。箭头表示向上(橙色)和向下(蓝色)移动鼠标时相机的旋转方式。

如您所见,只要偏航角为零,则旋转是正确的。但是偏航越多,相机旋转的圆圈就越小。相比之下,圆圈应始终像经度一样沿着整个球体延伸。

我对四元数不是很熟悉,所以在这里问一下如何正确旋转它们。

【问题讨论】:

  • 鼠标坐标很可能在屏幕空间中,这意味着像这样使用它们几乎不是一个好主意。相反,使用逆投影矩阵将它们转换为世界坐标。
  • 我的想法是使用鼠标坐标xy 作为角度来围绕upright 向量旋转相机四元数。我认为在大多数游戏中都是这样。我不确定你的方法是否有效。你能再解释一下吗?

标签: c++ 3d camera rotation quaternions


【解决方案1】:

我发现了如何自行正确旋转四元数。关键是找到我想要旋转的轴的向量。当角度是围绕实际轴旋转的量时,它们用于从轴和角度创建四元数。

以下代码显示了我最终得到的结果。它还允许滚动相机,这在一段时间内可能会有用。

void Rotate(btVector3 Amount, float Sensitivity)
{
    // fetch current rotation
    btTransform transform = camera->getWorldTransform();
    btQuaternion rotation = transform.getRotation();

    // apply mouse sensitivity
    Amount *= Sensitivity;

    // create orientation vectors
    btVector3 up(0, 1, 0);
    btVector3 lookat = quatRotate(rotation, btVector3(0, 0, 1));
    btVector3 forward = btVector3(lookat.getX(), 0, lookat.getZ()).normalize();
    btVector3 side = btCross(up, forward);

    // rotate camera with quaternions created from axis and angle
    rotation = btQuaternion(up,      Amount.getY()) * rotation;
    rotation = btQuaternion(side,    Amount.getX()) * rotation;
    rotation = btQuaternion(forward, Amount.getZ()) * rotation;

    // set new rotation
    transform.setRotation(rotation);
    camera->setWorldTransform(transform);
}

由于我很少找到关于四元数旋转的信息,所以我将花一些时间进一步解释上面的代码。

获取和设置旋转是特定于物理引擎的,与这个问题无关,所以我不会详细说明。下一部分,将数量乘以鼠标灵敏度应该非常清楚。让我们继续研究方向向量。

  • up 向量取决于您自己的实现。最方便的是,正 Y 轴指向上方,因此我们最终得到 0, 1, 0
  • lookat 向量表示相机观察的方向。我们只需通过相机旋转四元数旋转一个指向前方的单位向量。同样,前向矢量取决于您的约定。如果 Y 轴向上,则正 Z 轴可能指向前方,即0, 0, 1
  • 不要将它与下一个向量混淆。它被命名为forward,它引用了相机旋转。因此我们只需要将lookat 向量投影到地面。在这种情况下,我们只需采用lookat 向量并忽略向上指向的分量。为了整洁,我们对该向量进行归一化。
  • side 向量从相机方向指向左侧。因此它垂直于upforward 向量,我们可以使用cross product 来计算它。

给定这些向量,我们可以正确地围绕它们旋转相机四元数。您从哪个开始,Z、Y 或 Z,取决于 Euler angle sequence,这又是一个因应用程序而异的约定。由于我想以 Y X Z 顺序应用旋转,因此我执行以下操作。

  • 首先,围绕up 轴将相机旋转Y 轴旋转量。这是偏航。
  • 然后围绕指向左侧的side 轴旋转X 量。这是音高。
  • 最后,围绕 forward 向量旋转 Z 量以应用滚动。

要应用这些旋转,我们需要将由轴和角度创建的四元数与当前相机旋转相乘。最后,我们将得到的四元数应用于物理模拟中的身体。

【讨论】:

    【解决方案2】:

    矩阵和俯仰/偏航/滚动都有其局限性,我不再使用它们,而是使用四元数。我旋转视图向量并首先重新计算相机向量,然后是关于旋转视图向量的视图矩阵。

    void Camera::rotateViewVector(glm::quat quat) {
    
        glm::quat rotatedViewQuat;
    
        quat = glm::normalize(quat);
        m_viewVector = glm::normalize(m_viewVector);
    
        glm::quat viewQuat(0.0f,
            m_viewVector.x,
            m_viewVector.y,
            m_viewVector.z);
    
        viewQuat = glm::normalize(viewQuat);
    
        rotatedViewQuat = (quat * viewQuat) * glm::conjugate(quat);
        rotatedViewQuat = glm::normalize(rotatedViewQuat);
    
        m_viewVector = glm::normalize(glm::vec3(rotatedViewQuat.x, rotatedViewQuat.y, rotatedViewQuat.z));
        m_rightVector = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_viewVector));
        m_upVector = glm::normalize(glm::cross(m_viewVector, m_rightVector));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 2011-05-25
      • 2018-11-17
      • 2013-04-17
      相关资源
      最近更新 更多