我想编码第一人称相机,其旋转存储在四元数中。不幸的是,轮换有问题。
以下功能负责旋转相机。参数Mouse
和Speed
传递鼠标移动和旋转速度。然后该函数获取旋转四元数,旋转它并存储结果。顺便说一句,我正在使用类型和函数来自的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);
}
为了说明鼠标移动时产生的相机旋转,我给你看一个手绘图。在左侧显示相机实际执行的错误旋转。在右侧显示了所需的正确情况。箭头表示向上移动鼠标(橙色)和向下移动(蓝色)时相机的旋转方式。
如您所见,只要偏航为零,旋转就是正确的。但是它的偏航越多,相机旋转的圆就越小。相反,圆圈应该像经度一样沿着整个球体运行。
我对四元数不太熟悉,所以我在这里问如何正确旋转它们。
答案 0 :(得分:9)
我发现了如何正确地自行旋转四元数。关键是找到我想要旋转的轴的矢量。当角度是围绕实际轴旋转的量时,它们用于从轴和角度创建四元数。
以下代码显示了我最终的结果。它还允许滚动相机,这可能有用。
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
向量指向相机方向的左侧。因此,它垂直于up
和forward
向量,我们可以使用cross product来计算它。鉴于这些矢量,我们可以正确旋转它们周围的相机四元数。你从哪个开始,Z,Y或Z,取决于Euler angle sequence,这也是一个从应用程序到应用程序不同的约定。由于我想要以Y X Z顺序应用旋转,我执行以下操作。
up
轴旋转相机Y旋转量。这是偏航。side
轴旋转X个量。这是投球。forward
向量旋转Z量以应用滚动。要应用这些旋转,我们需要将轴和角度创建的四元数与当前相机旋转相乘。最后,我们将结果四元数应用于物理模拟中的主体。
答案 1 :(得分: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));
}