DirectX11四元数相机调整俯仰偏航时出现滚转问题求助
问题:四元数实现相机俯仰/偏航时出现滚转现象
我正在用DirectX11开发一款小型引擎,将Transform类的旋转实现从纯欧拉角改为四元数后,遇到了问题:仅调整相机的俯仰(pitch)和偏航(yaw)时,相机最终会出现滚转(roll)现象。尝试调整矩阵与四元数的乘法顺序后,问题仍未解决。
相关代码
相机旋转处理代码:
void FreeMovement::HandleRotation(float deltaTime) { // Get data Transform* pTransform = GetGameObject()->GetTransform(); InputManager* pInput = InputManager::GetInstance(); Rotator cameraRotation = pTransform->GetLocalRotation(); const float deltaAngle = m_RotateSpeed * deltaTime; // Handle input if (pInput->IsKeyPressed(VK_LEFT)) { cameraRotation = Rotator::FromAxisAngle(Vector3::Up(), -deltaAngle) * cameraRotation; } if (pInput->IsKeyPressed(VK_RIGHT)) { cameraRotation = Rotator::FromAxisAngle(Vector3::Up(), deltaAngle) * cameraRotation; } if (pInput->IsKeyPressed(VK_UP)) { cameraRotation = Rotator::FromAxisAngle(Vector3::Right(), -deltaAngle) * cameraRotation; } if (pInput->IsKeyPressed(VK_DOWN)) { cameraRotation = Rotator::FromAxisAngle(Vector3::Right(), deltaAngle) * cameraRotation; } // Set data cameraRotation.Normalize(); pTransform->SetLocalRotation(cameraRotation); }
FromAxisAngle与Rotator乘法实现:
DirectX_Rotator DirectX_Rotator::FromAxisAngle(const Vector3& axis, float angle) { return DirectX_Rotator{ DirectX::XMQuaternionRotationAxis(axis.GetVector(), angle * toRadians) }; } DirectX_Rotator DirectX_Rotator::operator* (const DirectX_Rotator& other) const { return DirectX_Rotator{ DirectX::XMQuaternionMultiply(GetVector(), other.GetVector()) }; }
注:Rotator与DirectX_Rotator是通过using关键字关联的别名,用于适配不同渲染API。
问题原因
核心问题在于你使用了全局坐标系的Up/Right轴生成旋转四元数,再与当前旋转相乘。当相机存在俯仰角度后,全局Right轴和相机自身的局部Right轴不再重合,此时用全局Right轴做俯仰旋转,会额外引入滚转分量,最终导致相机出现不必要的滚转。
解决方案
1. 改用相机局部坐标系的轴生成旋转
从当前相机的旋转中提取局部Up/Right方向,而非使用全局轴。可以通过将全局轴乘以当前相机旋转的逆矩阵,或直接用四元数将全局轴旋转到相机局部空间。
2. 调整四元数乘法顺序
DirectX的XMQuaternionMultiply(q1, q2)表示先应用q2的旋转,再应用q1的旋转。要让新旋转基于相机当前的局部空间生效,需将新生成的局部旋转四元数右乘到当前旋转上。
修改后的代码示例
void FreeMovement::HandleRotation(float deltaTime) { Transform* pTransform = GetGameObject()->GetTransform(); InputManager* pInput = InputManager::GetInstance(); Rotator cameraRotation = pTransform->GetLocalRotation(); const float deltaAngle = m_RotateSpeed * deltaTime; // 获取相机局部坐标系的Up和Right轴 Vector3 localUp = cameraRotation.RotateVector(Vector3::Up()); Vector3 localRight = cameraRotation.RotateVector(Vector3::Right()); // 处理偏航(Yaw):绕局部Up轴旋转 if (pInput->IsKeyPressed(VK_LEFT)) { Rotator yawRot = Rotator::FromAxisAngle(localUp, -deltaAngle); cameraRotation = cameraRotation * yawRot; // 右乘,基于局部空间叠加旋转 } if (pInput->IsKeyPressed(VK_RIGHT)) { Rotator yawRot = Rotator::FromAxisAngle(localUp, deltaAngle); cameraRotation = cameraRotation * yawRot; } // 处理俯仰(Pitch):绕局部Right轴旋转 if (pInput->IsKeyPressed(VK_UP)) { Rotator pitchRot = Rotator::FromAxisAngle(localRight, -deltaAngle); cameraRotation = cameraRotation * pitchRot; } if (pInput->IsKeyPressed(VK_DOWN)) { Rotator pitchRot = Rotator::FromAxisAngle(localRight, deltaAngle); cameraRotation = cameraRotation * pitchRot; } cameraRotation.Normalize(); pTransform->SetLocalRotation(cameraRotation); }
补充说明
- 需要实现
Rotator::RotateVector方法,内部调用DirectX的XMVector3Rotate,用当前四元数旋转向量。 - 建议限制俯仰角度(比如±89度),避免相机过度翻转导致的异常行为。
内容的提问来源于stack exchange,提问作者RenzoDP
相关产品推荐
相关产品推荐

