Quaternion camera. How do I make it rotate correct

2019-03-19 09:41发布

I have a 3D camera with its current rotation stored as a quaternion, and I'm having trouble rotating it correctly. I want the camera to rotate incrementally around its local axes based on mouse movement each frame (first-person-shooter-style), but the rotation is wrong. It kind of works, but the camera seems to "roll" around its forward axis when it shouldn't.

I update the rotation each frame with this function:

void Camera::rotate(const Quat& q)
{
    // m_rot is the current rotation
    m_rot = m_rot * q;
}

Here's my quaternion multiplication function:

Quat Quat::operator*(const Quat &rhs) const
{
    // quaternion elements in w,x,y,z order
    Vector4d res;

    res[0] = m_q[0]*rhs.m_q[0] - m_q[1]*rhs.m_q[1] -
             m_q[2]*rhs.m_q[2] - m_q[3]*rhs.m_q[3];
    res[1] = m_q[0]*rhs.m_q[1] + m_q[1]*rhs.m_q[0] +
             m_q[2]*rhs.m_q[3] - m_q[3]*rhs.m_q[2];
    res[2] = m_q[0]*rhs.m_q[2] - m_q[1]*rhs.m_q[3] +
             m_q[2]*rhs.m_q[0] + m_q[3]*rhs.m_q[1];
    res[3] = m_q[0]*rhs.m_q[3] + m_q[1]*rhs.m_q[2] -
             m_q[2]*rhs.m_q[1] + m_q[3]*rhs.m_q[0];

    return Quat(res);
}

Am I doing something wrong, or is this some kind of floating-point error thing?

2条回答
The star\"
2楼-- · 2019-03-19 10:12

Figured out the problem. For a mouse-controled first-person camera like the one I'm going for, I want to rotate around the local x-axis to look up and down, but the global y-axis for looking side to side.

So this is correct for the x-axis:

m_rot = m_rot * q;

But I need to do this for the y-axis:

m_rot = d * m_rot;
查看更多
混吃等死
3楼-- · 2019-03-19 10:32

Because I could not find any working example how to set up a first person camera using DirectXMath and because I spent two days working on my solution I decided to post my solution here. Perhaps there is anyone who is working on the same problem. My solution is not that optimized but the math behind it should be correct.

inline DX11FRAMEWORK_API DirectX::XMFLOAT4X4 MatrixCameraFirstPersonQuaternion(DirectX::XMFLOAT3 &Pos, DirectX::XMFLOAT3 &DeltaPos, DirectX::XMFLOAT3 &DeltaAngles,
        DirectX::XMVECTOR &RotationQuaternion, DirectX::XMFLOAT3 *At = nullptr, DirectX::XMFLOAT3 *Up = nullptr)
{
    using namespace DirectX;

    static const XMFLOAT3 OriginalAt = { 1.f, 0.f, 0.f };
    static const XMFLOAT3 OriginalUp = { 0.f, 1.f, 0.f };
    static const XMFLOAT3 OriginalRight = { 0.f, 0.f, 1.f };

    // performing rotation of x-axis (here roll) und z-axis (here pitch) round camera axis using quaternion
    RotationQuaternion = XMQuaternionMultiply(RotationQuaternion, XMQuaternionRotationRollPitchYaw(DeltaAngles.z, 0.f, -DeltaAngles.x));

    // performing rotation of y-axis (yaw) round world axis
    XMMATRIX MRotation = XMMatrixMultiply(XMMatrixRotationQuaternion(RotationQuaternion), XMMatrixRotationRollPitchYaw(0.f, -DeltaAngles.y, 0.f));

    // keep track of rotation round y-axis because it is rotated round world axis
    DeltaAngles = { 0.f, DeltaAngles.y, 0.f };

    // generating camera axis
    XMFLOAT3 CameraAt, CameraRight, CameraUp;
    XMStoreFloat3(&CameraAt, XMVector3TransformCoord(XMLoadFloat3(&OriginalAt), MRotation));
    XMStoreFloat3(&CameraRight, XMVector3TransformCoord(XMLoadFloat3(&OriginalRight), MRotation));
    XMStoreFloat3(&CameraUp, XMVector3TransformCoord(XMLoadFloat3(&OriginalUp), MRotation));

    // performing translation
    Pos += CameraAt * DeltaPos.x;
    Pos += CameraUp * DeltaPos.y;
    Pos += CameraRight * DeltaPos.z;
    DeltaPos = { 0.f, 0.f, 0.f };

    CameraAt += Pos;

    if (At)
        *At = CameraAt;
    if (Up)
        *Up = CameraUp;

    // finally generate view matrix
    DirectX::XMFLOAT4X4 Camera;
    DirectX::XMStoreFloat4x4(&Camera, DirectX::XMMatrixLookAtLH(DirectX::XMLoadFloat3(&Pos), DirectX::XMLoadFloat3(&CameraAt), DirectX::XMLoadFloat3(&CameraUp)));
    return Camera;
}
查看更多
登录 后发表回答