# DirectX Smooth Third-Person Camera

This topic is 1004 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello all!

I'm attempting to write a smooth third-person camera for a DirectX game I am making. My implementation uses a camera target that floats above the player's head, lerping to the player's X and Z location when the player moves. The camera itself is always a set distance away from the target, and calls "LookAt" on the target.

The camera currently follows the player smoothly upon translations, but rotations were jittery so I wanted to use quaternions and SLERP to make a smoother rotation around the player. However, with my current implementation below I seem to get strange rotations the more I rotate the camera. This leads me to believe that I am using the wrong axes or am encountering gimbal lock. Is there anything obviously wrong with my math? Or could it be a separate issue? Thanks in advance for your input.

Side Problem: How would I slerp between the two quaternions smoothly? What would I pass in for the ratio other than a fixed float? It seems impossible to slerp between two things in a single frame. I could do it over multiple frames, but the rotations would be reset after the first frame. So how would I accomplish this?

// Decompose the matrix to get the original rotation
XMVECTOR trans;
XMVECTOR scale;
XMVECTOR rotation;
XMVECTOR newRotation = rotation;

// Using the local X axis and the global Y for rotation; pitch and yaw are based on mouse rotation (delta)
XMVECTOR rotGlobalY = XMQuaternionRotationAxis(XMLoadFloat3(&XMFLOAT3(0.0f, 1.0f, 0.0f)), m_fYaw);

// Multiply the quaternions into the original rotation
newRotation = XMQuaternionMultiply(newRotation, rotLocalX);
newRotation = XMQuaternionMultiply(newRotation, rotGlobalY);

// Sluuurrrrpp!
newRotation = XMQuaternionSlerp(rotation, newRotation, 0.5f);

// Generate a new matrix with the new rotation
XMMATRIX newMat = XMMatrixAffineTransformation(scale, XMLoadFloat3(&XMFLOAT3(0.0f, 0.0f, 0.0f)), newRotation, trans);

// Set local matrix to new matrix with new rotation?
m_camTarg.SetLocalMatrix(newMat);

// Move the camera based on the local axes of the camera target
Vector3 camOffsetDirection = Vector3::Zero;

camOffsetDirection += *m_camTarg.GetLocalXAxis() * m_camOffset.x; // m_camOffset = (0.0f, 0.0f, -7.0f)
camOffsetDirection += *m_camTarg.GetLocalYAxis() * m_camOffset.y;
camOffsetDirection += *m_camTarg.GetLocalZAxis() * m_camOffset.z;

SetPosition(*m_camTarg.GetPosition() + camOffsetDirection);

LookAt(*GetPosition(), *m_camTarg.GetPosition(), Vector3::UnitY);
Edited by Dakattack64

##### Share on other sites

Have you tried not using XMMatrixDecompose(), but storing camera trans+rot as vectors (you must have them since you're doing a LookAt() to build the matrix)?

My thought is that the decomposed angles aren't correct, maybe because of angle limitations inherent from decomposition?

You could also try monitoring the angles output from XMMatrixDecompose() and newRotation and see what's happening?

Or maybe you could try XMQuaternionRotationRollPitchYaw(0, m_fPitch, m_fYaw) instead of those two rotations you're doing? - The angles you rotate about seems strange to me, one is straight (0, 1, 0) but the other is potentially not at a straight angle from the straight one (which in my book would give strange rotations)..

##### Share on other sites

Have you tried not using XMMatrixDecompose(), but storing camera trans+rot as vectors (you must have them since you're doing a LookAt() to build the matrix)?

My thought is that the decomposed angles aren't correct, maybe because of angle limitations inherent from decomposition?

You could also try monitoring the angles output from XMMatrixDecompose() and newRotation and see what's happening?

Or maybe you could try XMQuaternionRotationRollPitchYaw(0, m_fPitch, m_fYaw) instead of those two rotations you're doing? - The angles you rotate about seems strange to me, one is straight (0, 1, 0) but the other is potentially not at a straight angle from the straight one (which in my book would give strange rotations)..

I have peeked inside the newRotation and they look like valid values. In a previous implementation I was using XMQuaternionRotationRollPitchYaw() but because it uses world axes it wasn't rotating about the local X. This would cause problems where I wouldn't be able to rotate around the X axis when facing the player's left or right side (because I would be looking straight down the world X).

Hence, the reason why I am using the two separate axes for rotation; you want to rotate around the global Y axis, but the local X axis. Using global X won't allow us to change our pitch in all orientations, and using local Y will cause us to orbit in a strange manner. I could probably explain it better on paper.

I think that my problem might be caused by large delta mouse movements when the cursor goes outside of the screen, as the camera looks fine until I move it outside of the window. I'm going to see if setting the cursor to the center of the window every frame fixes the issue.

##### Share on other sites

Ahh I think I get it now :)

For mouse delta movement, I use this method:

	RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01;
rid.usUsage = 0x02;
rid.dwFlags = RIDEV_NOLEGACY;
rid.hwndTarget = Global_Hwnd;
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)) == FALSE)
return ERR_ALLOCATION;


// .. This is in your WndProc()

case WM_INPUT:
{
UINT size;
if (size > 0)
{
RAWINPUT *data = (RAWINPUT*)malloc(size);
if (data)
{
{
if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
{
MouseX = data->data.mouse.lLastX;
MouseY = data->data.mouse.lLastY;
}
else
{
MouseX += data->data.mouse.lLastX;
MouseY += data->data.mouse.lLastY;
}
}
free(data);
}
}
return 0;
}


It allows me to read mouse position without being clipped :D

Before that, I used to center the mouse too, but it's a bad solution to the problem in the long run :)

1. 1
2. 2
Rutin
17
3. 3
4. 4
5. 5

• 13
• 26
• 10
• 11
• 9
• ### Forum Statistics

• Total Topics
633735
• Total Posts
3013593
×