# How to get rid of camera z-axis rotation

## Recommended Posts

BlackJoker    1328

Hello.

I have an issue with my quternion based camera: when I move it only on X and Y axis fast it is also begin rotate around Z axis, but it shouldnt.

I use sharpDX lib and my code is following:

public void RotateX(float _degree_angle)
{
if (cameraType == CamType.ThirdPerson)
{
_degree_angle = -_degree_angle;
}

UpdateViewMatrix();
}

public void RotateY(float _degree_angle)
{
if (cameraType == CamType.ThirdPerson)
{
_degree_angle = -_degree_angle;
}

UpdateViewMatrix();
}


private void UpdateViewMatrix()
{
qRotation = Quaternion.Normalize(qRotation);

if (CameraType == CamType.ThirdPerson)
{
ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationQuaternion(qRotation);

position -= new Vector3(ViewMatrix.M13, ViewMatrix.M23, ViewMatrix.M33) * radius;

ViewMatrix = Matrix.LookAtLH(position, lookAt, new Vector3(ViewMatrix.M12, ViewMatrix.M22, ViewMatrix.M32));
}
else
{
ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationQuaternion(qRotation);
}
}


I tried to change order of multiplying for x and y quaternion rotations like this:

qRotation = Quaternion.Multiply(Quaternion.RotationAxis(Vector3.UnitY, MathUtil.DegreesToRadians(_degree_angle)), qRotation);

qRotation = Quaternion.Multiply(qRotation, Quaternion.RotationAxis(Vector3.UnitX, MathUtil.DegreesToRadians(_degree_angle2))); 

This helps - after that camera no longer rotate on Z-axis during fast moving on x-y axis, but if I move it on diagonal, if begins to rotate around all 3 axis, describing virtual 8 digit.

Could someone please point me the right way working with quaternions to stop camera rotation around Z-axis when I dont need this?

This issue is driving me crazy.

##### Share on other sites
alek314??    398

I use front, side and up vector for my camera. after each update, I will set the y component of the side vector zero, so that no rotation around z axis is possible. After that, I will normalize the vector and recompute the front and up vector.

##### Share on other sites
BlackJoker    1328

Could you please give the code responcible for recomputing the front and up vectors?

Or (better) the whole view matrix update.

##### Share on other sites
alek314??    398

D3DXVECTOR3 eye; //camera position

D3DXVECTOR3 at; //where you look at

D3DXVECTOR3 up(0,1,0); //assume the up vector is always pointing up, could have problem when you look straight up

D3DXVECTOR3 front = Normalize(at - eye);

D3DXVECTOR3 side = cross(front,up); //if you look through front vector, side vector will be on your right

use D3DXMatrixLookAtLH(....) or XMMatrixLookAtLH(...) to update the view matrix

##### Share on other sites
BlackJoker    1328

Thanks, but side and front is not using in XMMatrixLookAtLH(...) or D3DXMatrixLookAtLH(...). Where and when I need to apply them?

Edited by BlackJoker

##### Share on other sites
Elzianor    121

D3DXVECTOR3 eye; //camera position

D3DXVECTOR3 at; //where you look at

D3DXVECTOR3 up(0,1,0); //assume the up vector is always pointing up, could have problem when you look straight up

D3DXVECTOR3 front = Normalize(at - eye);

D3DXVECTOR3 side = cross(front,up); //if you look through front vector, side vector will be on your right

use D3DXMatrixLookAtLH(....) or XMMatrixLookAtLH(...) to update the view matrix

Doesn't D3DXMatrixLookAtLH(....) or XMMatrixLookAtLH(...) take as income parameters the mentioned 'eye', 'at' and 'up' and construct 'front' and 'side' automatically?

##### Share on other sites
alvaro    21246
If you rotate around the X axis, then rotate around the Y axis, then undo the rotation around the X axis, and finally undo the rotation around the Y axis, the resulting transformation is a rotation around the Z axis. This is a feature of rotations in 3D space, and you will observe it regardless of whether you use quaternions or anything else to represent them.

Perhaps instead of rotating around the X and Y axes, you meant to control pitch and yaw. If that's the case, use those angles to build the rotation from scratch every time.

##### Share on other sites
alek314??    398

D3DXVECTOR3 eye; //camera position

D3DXVECTOR3 at; //where you look at

D3DXVECTOR3 up(0,1,0); //assume the up vector is always pointing up, could have problem when you look straight up

D3DXVECTOR3 front = Normalize(at - eye);

D3DXVECTOR3 side = cross(front,up); //if you look through front vector, side vector will be on your right

use D3DXMatrixLookAtLH(....) or XMMatrixLookAtLH(...) to update the view matrix

Doesn't D3DXMatrixLookAtLH(....) or XMMatrixLookAtLH(...) take as income parameters the mentioned 'eye', 'at' and 'up' and construct 'front' and 'side' automatically?

After 'side = cross(front,up)', set 'side.y = 1', then normalize it, then recompute the up vector.

At this time, 'eye' and 'at' haven't changed, but 'up' has changed.

If you have rotation angle, álvaro's method would be simpler.

##### Share on other sites
BlackJoker    1328

If you rotate around the X axis, then rotate around the Y axis, then undo the rotation around the X axis, and finally undo the rotation around the Y axis, the resulting transformation is a rotation around the Z axis. This is a feature of rotations in 3D space, and you will observe it regardless of whether you use quaternions or anything else to represent them.

Perhaps instead of rotating around the X and Y axes, you meant to control pitch and yaw. If that's the case, use those angles to build the rotation from scratch every time.

We already tried to accum degrees, but as a result, camera moving not on orbit, but on trajectory 8  (rotating in all 3 axis at the same time).

Edited by BlackJoker

##### Share on other sites

If you're trying to do a 3rd person camera, I'll echo Álvaro and suggest you just keep track of camera yaw and position then construct the transform each frame.

Pseudo code:

class Camera
{
float m_yaw; // Should be a value between 0 and 2 PI
float m_pitch; // Value between -PI/2 and PI/2
Vector3 m_position;
Matrix  m_cameraMatrix;

void Update()
{
Vector2 deltaMousePos; // Assume this is a 2D vector that has the delta X/Y of our mouse cursor since the last frame.

m_yaw += deltaMousePos.x;
m_pitch += deltaMousePos.y;

// Construct our rotation based on our current yaw/pitch
Quaternion yRotation(Vector3::UnitY, m_yaw);
Quaternion xRotation(Vector3::UnitX, m_pitch);

m_cameraMatrix = Matrix::Identity;
m_cameraMatrix *= (xRotation * yRotation);
m_cameraMatrix.translation = m_position; // Restore our position.
}

}


##### Share on other sites
BlackJoker    1328

I think we misunderstood each other:

I have free or whatever camera:

I calculate its rotation like this:

float heading = XMConvertToRadians(headingDegrees);

XMVECTOR rot;

/*m_orientation = XMQuaternionMultiply(m_orientation, rot);*/

{
m_orientation = XMQuaternionMultiply(rot, m_orientation);
}

// Rotate camera about its local x axis.
// Note the order the quaternions are multiplied. That is important!
if (pitch != 0.0f)
{
m_orientation=XMQuaternionMultiply(m_orientation, rot);
}


Order of quaternion multiplication is changed because other way camera will rotate around Z-axis on each move.

So, now everything is ok, BUT...

If I move camera on diagonal in any side it will flip on 180 degrees after full turn and instead of normal view, I will receive exactly 180 degrees flipped camera view.

And its not all...

If I leave right quaternion multiplication order, There is no flipping at all while camera moving on diagonal, but camera become to rotate around Z-axis while fast moving on x-y axis.

So, solving one issue I receive another and vice versa.

My question is: How to get rid of both issues?

For now I have no idea how to lock camera flipping around while moving on diagonal direction.

Can you help me to solve this issue or this is not possible and I must tolerate and live with it?

Edited by BlackJoker

##### Share on other sites
alvaro    21246
Are headingDegrees, pitchDegrees and rollDegrees accumulators, or are they an instantaneous indication of how you are rotating?

##### Share on other sites
BlackJoker    1328

Are headingDegrees, pitchDegrees and rollDegrees accumulators, or are they an instantaneous indication of how you are rotating?

This is just delta mouse position * rotation speed:

heading = -mouse.xPosRelative() * rotationSpeed;
pitch = -mouse.yPosRelative() * rotationSpeed;
roll = direction.x *cameraDX_Free->getCameraSpeedFlight() * elapsedTimeSec;


##### Share on other sites

Even if you are using a free camera, I'm not sure why you would ever want roll (but it looks like you aren't actually doing anything with that value). As Álvaro said, you should probably be using those values as accumulators rather than just delta rotation angles. Otherwise if you are just applying those delta rotations each frame you're going to be rotating in whatever space you were previously in which could cause the weird behavior you are seeing - hence building the camera matrix EVERY frame so you keep your transform space "clean" since you are just rebuilding it based on a few values.

class Camera
{
//... previous code

void rotate(float yaw, float pitch)
{
m_yaw += yaw;
// Wrap around if we exceed 2 PI, or go below 0
if (m_yaw > TWO_PI)
{
m_yaw -= TWO_PI;
}
else if (m_yaw < 0.0f)
{
m_yaw += TWO_PI;
}

m_pitch += pitch
// With pitch, we probably dont want to wrap around as that would cause some ugliness, so just clamp.
if (m_pitch > HALF_PI)
{
m_pitch = HALF_PI;
}
else if (m_pitch < -HALF_PI)
{
m_pitch = -HALF_PI;
}
}
}


##### Share on other sites
alvaro    21246
I am not sure what was wrong with my suggestion, which AdeptStrain explained perfectly well.

There is another thing you could do, which might be a smaller change to how you are doing things: If you want your orientation to maintain a certain up vector, just enforce that in every step. To do that you'd multiply m_orientation' by the smallest rotation that maps the current up vector to the desired up vector (probably on the left, but I would have to think about it).

##### Share on other sites
BlackJoker    1328

Even if you are using a free camera, I'm not sure why you would ever want roll (but it looks like you aren't actually doing anything with that value). As Álvaro said, you should probably be using those values as accumulators rather than just delta rotation angles. Otherwise if you are just applying those delta rotations each frame you're going to be rotating in whatever space you were previously in which could cause the weird behavior you are seeing - hence building the camera matrix EVERY frame so you keep your transform space "clean" since you are just rebuilding it based on a few values.

class Camera
{
//... previous code

void rotate(float yaw, float pitch)
{
m_yaw += yaw;
// Wrap around if we exceed 2 PI, or go below 0
if (m_yaw > TWO_PI)
{
m_yaw -= TWO_PI;
}
else if (m_yaw < 0.0f)
{
m_yaw += TWO_PI;
}

m_pitch += pitch
// With pitch, we probably dont want to wrap around as that would cause some ugliness, so just clamp.
if (m_pitch > HALF_PI)
{
m_pitch = HALF_PI;
}
else if (m_pitch < -HALF_PI)
{
m_pitch = -HALF_PI;
}
}
}


I already tried this, but this has no effect. Described Issues still present.

I think problem is that in curtain moment cameras UP vector become something like 0,-1,0 instead of 0,1,0

So, my question here, how to set up correct UP vector after my manipulations with quaternions so, that no to break the view matrix?

Or this is issue with wrog view direction, because when I made 180 degrees rotation on diagonal, I still see the model. but I must not see it, because camera must look at the another point opposit to the model.

How do you think?

Edited by BlackJoker

##### Share on other sites
alvaro    21246

Even if you are using a free camera, I'm not sure why you would ever want roll (but it looks like you aren't actually doing anything with that value). As Álvaro said, you should probably be using those values as accumulators rather than just delta rotation angles. Otherwise if you are just applying those delta rotations each frame you're going to be rotating in whatever space you were previously in which could cause the weird behavior you are seeing - hence building the camera matrix EVERY frame so you keep your transform space "clean" since you are just rebuilding it based on a few values.

class Camera
{
//... previous code

void rotate(float yaw, float pitch)
{
m_yaw += yaw;
// Wrap around if we exceed 2 PI, or go below 0
if (m_yaw > TWO_PI)
{
m_yaw -= TWO_PI;
}
else if (m_yaw < 0.0f)
{
m_yaw += TWO_PI;
}

m_pitch += pitch
// With pitch, we probably dont want to wrap around as that would cause some ugliness, so just clamp.
if (m_pitch > HALF_PI)
{
m_pitch = HALF_PI;
}
else if (m_pitch < -HALF_PI)
{
m_pitch = -HALF_PI;
}
}
}


I already tried this, but this has no effect. Described Issues still present.

You are going to have to post some code to go with that. Building a rotation from scratch on every frame from yaw and pitch can't possibly leave you with a rotation around the z axis.

##### Share on other sites
BlackJoker    1328

Here is my code:

take degrees for X and Y

if (mouseInput.MouseState.RightButton.Down)
{
basicCamera.RotateX(-mouseInput.MouseState.DeltaX * mouseRotationSpeed);
basicCamera.RotateY(-mouseInput.MouseState.DeltaY * mouseRotationSpeed);

}


Then do quaternion multiply:

public void RotateX(float _degree_angle)
{
if (cameraType == CamType.ThirdPerson)
{
_degree_angle = -_degree_angle;
}

UpdateViewMatrix();
}

public void RotateY(float _degree_angle)
{
if (cameraType == CamType.ThirdPerson)
{
_degree_angle = -_degree_angle;
}

UpdateViewMatrix();
}


FInally update view matrix:

private void UpdateViewMatrix()
{
qRotation = Quaternion.Normalize(qRotation);

if (CameraType == CamType.ThirdPerson)
{
ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationQuaternion(qRotation);

position -= new Vector3(ViewMatrix.M13, ViewMatrix.M23, ViewMatrix.M33) * radius;

ViewMatrix = Matrix.LookAtLH(position, lookAt, new Vector3(ViewMatrix.M12, ViewMatrix.M22, ViewMatrix.M32));
}
else
{
ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationQuaternion(qRotation);
}
}


P.S. quaternion multiply order in Y rotation changed because it prevent camera to move around its z-axis, but it causes it to turn upside down when moving on diagonal. If I dont change the order, then moving on diagonal is good, but camera is rotating on z-axis each time I move it around x and y axis.

##### Share on other sites

Here is my code:

take degrees for X and Y

if (mouseInput.MouseState.RightButton.Down)
{
basicCamera.RotateX(-mouseInput.MouseState.DeltaX * mouseRotationSpeed);
basicCamera.RotateY(-mouseInput.MouseState.DeltaY * mouseRotationSpeed);

}


Then do quaternion multiply:

public void RotateX(float _degree_angle)
{
if (cameraType == CamType.ThirdPerson)
{
_degree_angle = -_degree_angle;
}

UpdateViewMatrix();
}

public void RotateY(float _degree_angle)
{
if (cameraType == CamType.ThirdPerson)
{
_degree_angle = -_degree_angle;
}

UpdateViewMatrix();
}


FInally update view matrix:

private void UpdateViewMatrix()
{
qRotation = Quaternion.Normalize(qRotation);

if (CameraType == CamType.ThirdPerson)
{
ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationQuaternion(qRotation);

position -= new Vector3(ViewMatrix.M13, ViewMatrix.M23, ViewMatrix.M33) * radius;

ViewMatrix = Matrix.LookAtLH(position, lookAt, new Vector3(ViewMatrix.M12, ViewMatrix.M22, ViewMatrix.M32));
}
else
{
ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationQuaternion(qRotation);
}
}


P.S. quaternion multiply order in Y rotation changed because it prevent camera to move around its z-axis, but it causes it to turn upside down when moving on diagonal. If I dont change the order, then moving on diagonal is good, but camera is rotating on z-axis each time I move it around x and y axis.

You aren't actually creating accumulators and building the matrix each frame from scratch. You're modifying "qRotation" every time you call RotateX/RotateY, so if you rotate 45 degrees on the X and Y, next frame if you do the same you're going to be rotating from within that previous "diagonal" space. Building it from scratch prevents you from ever rotating outside of the unit X/Y/Z space.

##### Share on other sites
BlackJoker    1328

I still not really understand what do you mean from scratch. You mean I need to make qRotation Identity each time after multiply or what?

ViewMatrix is building from scratch each time.

Sorry, but I really dont understand for now your concept. Maybe if you could change my code with yours (even pseudo code) it will help me to understand at last.

Edited by BlackJoker

##### Share on other sites
alvaro    21246

I still not really understand what do you mean from scratch. You mean I need to make qRotation Identity each time after multiply or what?
ViewMatrix is building from scratch each time.

I think you are very close. What we are saying is that instead of representing the current state as qRotation', you should keep two accumulators called pitch' and yaw', of type float. You increment or decrement those in response to user input, and then build the ViewMatrix starting from the identity and applying the x-axis rotation and the y-axis rotation.

##### Share on other sites
BlackJoker    1328

I still not really understand what do you mean from scratch. You mean I need to make qRotation Identity each time after multiply or what?
ViewMatrix is building from scratch each time.

I think you are very close. What we are saying is that instead of representing the current state as qRotation', you should keep two accumulators called pitch' and yaw', of type float. You increment or decrement those in response to user input, and then build the ViewMatrix starting from the identity and applying the x-axis rotation and the y-axis rotation.

So, the code is the same, but I must keep angles and create quaternons from this angles?

Update:

I tried to accum angles, but this is completely not working or I just dont understand what how to rotate correctly, because camera moving like a crazy.

Edited by BlackJoker

##### Share on other sites
alvaro    21246

So, the code is the same, but I must keep angles and create quaternons from this angles?

Update:
I tried to accum angles, but this is completely not working or I just dont understand what how to rotate correctly, because camera moving like a crazy.

For the last time, please show us your code when you report a problem.

##### Share on other sites
BlackJoker    1328
                accumx += -mouseInput.MouseState.DeltaX * mouseRotationSpeed;
// Wrap around if we exceed 2 PI, or go below 0
if (accumx > 360)
{
accumx -= 360;
}
else if (accumx < 0.0f)
{
accumx += 360;
}

accumy += -mouseInput.MouseState.DeltaY * mouseRotationSpeed;
// With pitch, we probably dont want to wrap around as that would cause some ugliness, so just clamp.
if (accumy > 90)
{
accumy = 90;
}
else if (accumy < -90)
{
accumy = -90;
}
basicCamera.RotateX(accumy);
basicCamera.RotateY(accumx);

accumx = MathUtil.DegreesToRadians(_degree_angle);

ViewMatrix = Matrix.Translation(Vector3.Negate(position)) * Matrix.RotationX(accumx) * Matrix.RotationY(accumy);

`

But this is not solving issue with flipping.It is the same as with quaternions.

##### Share on other sites
BlackJoker    1328

I think the only problem here is how to compensate rotation for camera z-axis?

Is there any way to always keep camera strictly on Z-axis not letting to roll anywhere?

If I can solve this problem, issue will gone.

Edited by BlackJoker