6

我想编写一个第一人称相机,其旋转存储在一个四元数中。不幸的是,旋转有问题。

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

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

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

左边的错误旋转和右边的所需旋转

可以看到,只要偏航角为零,旋转就是正确的。但是偏航越多,相机旋转的圆圈就越小。相反,圆圈应始终像经度一样沿着整个球体延伸。

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

4

2 回答 2

9

I found out how to properly rotate a quaternion on my own. The key was to find vectors for the axis I want to rotate around. Those are used to create quaternions from axis and angle, when angle is the amount to rotate around the actual axis.

The following code shows what I ended up with. It also allows to roll the camera, which might be useful some time.

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

Since I rarely found information about quaternion rotation, I'll spend some time further explaining the code above.

Fetching and setting the rotation is specific to the physics engine and isn't related to this question so I won't elaborate on this. The next part, multiplying the amount by a mouse sensitivity should be really clear. Let's continue with the direction vectors.

  • The up vector depends on your own implementation. Most conveniently, the positive Y axis points up, therefore we end up with 0, 1, 0.
  • The lookat vector represents the direction the camera looks at. We simply rotate a unit vector pointing forward by the camera rotation quaternion. Again, the forward pointing vector depends on your conventions. If the Y axis is up, the positive Z axis might point forward, which is 0, 0, 1.
  • Do not mix that up with the next vector. It's named forward which references to the camera rotation. Therefore we just need to project the lookat vector to the ground. In this case, we simply take the lookat vector and ignore the up pointing component. For neatness we normalize that vector.
  • The side vector points leftwards from the camera orientation. Therefore it lies perpendicular to both the up and the forward vector and we can use the cross product to compute it.

Given those vectors, we can correctly rotate the camera quaternion around them. Which you start with, Z, Y or Z, depends on the Euler angle sequence which is, again, a convention varying from application to application. Since I want to rotations to be applied in Y X Z order, I do the following.

  • First, rotate the camera around the up axis by the amount for the Y rotation. This is yaw.
  • Then rotate around the side axis, which points leftwards, by the X amount. It's pitch.
  • And lastly, rotate around the forward vector by the Z amount to apply roll.

To apply those rotations, we need to multiply the quaternions create by axis and angle with the current camera rotation. Lastly we apply the resulted quaternion to the body in the physics simulation.

于 2013-05-06T14:14:13.280 回答
0

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

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));
}
于 2017-03-24T03:31:26.337 回答