Jump to content

  • Log In with Google      Sign In   
  • Create Account

Quaternion camera performs unwanted roll


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
11 replies to this topic

#1 BlurEffect   Members   -  Reputation: 163

Like
0Likes
Like

Posted 20 February 2014 - 05:37 PM

Hey guys,

 

I've been trying to figure this out all day and I know that there quite some ressources on that rotation/quaternion stuff out there but I still couldn't figure out the problem. I'm creating an editor in DirectX 11 and are currently doing a camera that can be rotated and moved around using the mouse. Moving the camera works fine. For the rotation I want the user to be able to hold down the right mouse button and perform a free look around. In the code, I chose to use quaternions as I read that they would have quite some advantages compared to other rotation techniques. My code almost works as it should but, although the expression "input.m_rotation.z * ROTATION_SPEED.z" in the code below currently always evaluates to 0, there is still some roll occurring when I rotate the camera. I can't figure out where the problem lies, so I hope you have an idea and can show me what I'm missing or doing wrong. I don't really have experience in using quaternions, so if you can share some knowledge on how to improve the code below in general or on alternate implementations, that would be great, too.

 

(the "input" variable in the code contains the delta movement/rotation to be applied for the current frame based on user input)

XMVECTOR pitchQuaternion = XMQuaternionRotationAxis(XMLoadFloat3( &m_right ), XMConvertToRadians( input.m_rotation.x * ROTATION_SPEED.x));
XMVECTOR yawQuaternion = XMQuaternionRotationAxis(XMLoadFloat3( &m_up ), XMConvertToRadians( input.m_rotation.y * ROTATION_SPEED.y));
XMVECTOR rollQuaternion = XMQuaternionRotationAxis(XMLoadFloat3( &m_lookAt ), XMConvertToRadians( input.m_rotation.z * ROTATION_SPEED.z));

XMVECTOR rotationQuaternion = XMQuaternionMultiply(yawQuaternion, pitchQuaternion);
rotationQuaternion = XMQuaternionMultiply(rotationQuaternion, rollQuaternion);

XMMATRIX matTranslation = XMMatrixTranslationFromVector(-XMLoadFloat3(&input.m_movement) * XMLoadFloat3(&MOVEMENT_SPEED));
XMMATRIX matRotation = XMMatrixRotationQuaternion(XMQuaternionConjugate(rotationQuaternion));

XMMATRIX matTransform = XMMatrixMultiply(matTranslation, matRotation);
XMStoreFloat4x4(&m_viewMatrix, XMMatrixMultiply(XMLoadFloat4x4(&m_viewMatrix), matTransform));

If you need more information, please ask. Thank you very much in advance for your effort!



Sponsor:

#2 slayemin   Members   -  Reputation: 2911

Like
0Likes
Like

Posted 20 February 2014 - 06:10 PM

It looks very similar to the way I did my mouse look code as well. Here are the relevant snippets you might be interested in:

Camera3D:

/// <summary>
/// This increments the camera look-at position by the given radian angles:
///  [yaw, pitch, roll]
/// </summary>
/// <param name="d_xyz">these are the angles to change orientation by.</param>
public void RotateBy(Vector3 d_xyz)
{
m_rotation *= Quaternion.CreateFromAxisAngle(Vector3.Up, d_xyz.X) * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, d_xyz.Y) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, d_xyz.Z);

m_lookAt = m_position + Vector3.Transform(Vector3.UnitX, m_rotation);
m_up = Vector3.Transform(Vector3.Up, m_rotation);

UpdateView();
UpdateFrustum();
}

MouseLook Code:
 

if (Mouse.GetState().RightButton == ButtonState.Pressed)
{
	int yaw = Mouse.GetState().X - m_lastMouse.X;
	int pitch = Mouse.GetState().Y - m_lastMouse.Y;
	if (yaw != 0 || pitch != 0)
	{
		if (Keyboard.GetState().IsKeyDown(Keys.LeftShift))
		{
			m_sceneDB.ActiveCamera.RotateBy(new Vector3(MathHelper.ToRadians(-yaw / 10.0f), MathHelper.ToRadians(-pitch / 10.0f), 0));
		}
		else if (Keyboard.GetState().IsKeyDown(Keys.LeftControl))
		{
			m_sceneDB.ActiveCamera.RotateBy(new Vector3(MathHelper.ToRadians(-yaw * 3.0f), MathHelper.ToRadians(-pitch * 3.0f), 0));
		}
		else
		{
			m_sceneDB.ActiveCamera.RotateBy(new Vector3(MathHelper.ToRadians(-yaw), MathHelper.ToRadians(-pitch), 0));
		}
		
		Mouse.SetPosition(m_lastMouse.X, m_lastMouse.Y);
	}
}

I think one difference between my implementation and yours is that I am simply adding a delta rotation to an existing quaternion rotation and your implementation is creating a new quaternion rotation based on the yaw, pitch and roll values. I'm not sure if that's where the error is coming from, but that might be a good place to look first.

You can also decide to manually set the roll to zero each frame, but that's kind of a hackish solution.


Edited by slayemin, 20 February 2014 - 06:10 PM.

Eric Nevala

Indie Developer | Dev blog


#3 NumberXaero   Prime Members   -  Reputation: 1513

Like
0Likes
Like

Posted 20 February 2014 - 07:31 PM

Where are m_right, m_up, and m_lookAt coming from? Could we see where/how they get updated?



#4 cephalo   Members   -  Reputation: 575

Like
0Likes
Like

Posted 20 February 2014 - 09:52 PM

If you are using free look, and you look up a little and then move the mouse straight left, you will end up with the horizon slanted. Is that what you mean? To avoid that you have to take extra steps which I can't remember right now.



#5 BlurEffect   Members   -  Reputation: 163

Like
0Likes
Like

Posted 21 February 2014 - 11:44 AM

First of all: Thanks guys for your swift answers!

The question of NumberXaero brought me on the right track because currently I'm actually not updating the m_right, m_up, and m_lookAt vectors at all. That's probably the reason for the weird behaviour of the camera. I can't believe I didn't think of that myself, I assume I just sat too long in front of that code yesterday.

So, how can I update these vectors if I have a quaternion for the camera rotation? Can I simply multiply them with the quaternion?

Cheers guys!



#6 BlurEffect   Members   -  Reputation: 163

Like
0Likes
Like

Posted 21 February 2014 - 12:27 PM

Ok, I've figured it out myself, kind of...

Updating the lookAt and up vector for the given rotation quaternion works just fine and the camera seems to behave just as I want it to. But when I additionally update the right vector in a similar fashion (which I thought should also be necessary) I get this unwanted roll again when rotating the camera. I also tried to get the new right vector by calculating the cross product of up and lookAt vector but that didn't improve anything. I'm posting the code below. Any hints?

XMVECTOR v = XMQuaternionMultiply(rotationQuaternion, XMLoadFloat3( &m_up ));
XMStoreFloat3(&m_up, XMQuaternionMultiply(v, XMQuaternionConjugate(rotationQuaternion)));
	
v = XMQuaternionMultiply(rotationQuaternion, XMLoadFloat3( &m_lookAt ));
XMStoreFloat3(&m_lookAt, XMQuaternionMultiply(v, XMQuaternionConjugate(rotationQuaternion)));
	
// if I include the two lines below I get the unwanted roll, if I leave it commented out the camera seems to work properly
v = XMQuaternionMultiply(rotationQuaternion, XMLoadFloat3( &m_right ));
XMStoreFloat3(&m_right, XMQuaternionMultiply(v, XMQuaternionConjugate(rotationQuaternion)));

// This is what I tried as an alternative to the line above, with no success
XMStoreFloat3(&m_right, XMVector3Cross(XMLoadFloat3(&m_up), XMLoadFloat3(&m_lookAt)));


#7 slayemin   Members   -  Reputation: 2911

Like
0Likes
Like

Posted 21 February 2014 - 02:41 PM

I don't even use a right vector in my camera code. I figure that the "up" and "lookat" vectors are enough. If I need a right vector, I can derive it from those two vectors by using a cross product. The only time I'd really need a right/left vector is for moving the camera on its lateral axis. But, that can be accomplished with a transform as well. Here is my code for panning the camera which demonstrates this:

 

/// <summary>
/// This pans the camera along its left direction vector by an amount
/// </summary>
/// <param name="amount">The amount to pan by (use negative values to pan right)</param>
public void Pan(float amount)
{
	Vector3 DirVec = Vector3.Transform(Vector3.Forward, m_rotation) * amount;
	m_position += DirVec;
	m_lookAt += DirVec;
	UpdateView();
	UpdateFrustum();
}

Eric Nevala

Indie Developer | Dev blog


#8 BlurEffect   Members   -  Reputation: 163

Like
0Likes
Like

Posted 21 February 2014 - 03:05 PM

But what about the pitch rotation? I thought you would need the current "right" vector to properly calculate that rotation. It's just weird that it works perfectly without doing anything to the right vector and I don't like that I don't really understand it.



#9 NumberXaero   Prime Members   -  Reputation: 1513

Like
0Likes
Like

Posted 21 February 2014 - 04:23 PM

Ive had this problem before when using input values to incrementally rotate. The way I fixed it was to track the total rotation about each axis, and then rebuild the orientation each frame. So...

 

// Get Input
vec3 pitchYawRoll;   // add up your angles here, clamp, wrap, convert to radians, etc

// Updating
quat qPitch, qYaw;
qPitch.FromAxisAngle(UNIT_X, pitchYawRoll.x);        // build pitch
qYaw.FromAxisAngle(UNIT_Y, pitchYawRoll.y);          // build yaw

quat qPitchYaw = qYaw * qPitch;
vec3 forward = qPitchYaw * UNIT_Z;    // create forward

quat qRoll;
qRoll.FromAxisAngle(forward, pitchYawRoll.z);    // roll about current forward

quat final = qRoll * qPitchYaw;

 

// then if you want up and right

vec3 right = final * UNIT_X;

vec3 up = final * UNIT_Y;

// or calc 1 of the above and do cross product with forward to get the other

 

Your rotation order may be different, etc, but thats the general idea I use.


Edited by NumberXaero, 21 February 2014 - 04:24 PM.


#10 BlurEffect   Members   -  Reputation: 163

Like
0Likes
Like

Posted 23 February 2014 - 07:35 PM

I went with a similar solution to what NumberXaero posted and it worked fine. Thanks everybody for your effort!



#11 BornToCode   Members   -  Reputation: 948

Like
0Likes
Like

Posted 28 February 2014 - 12:44 AM

I went with a similar solution to what NumberXaero posted and it worked fine. Thanks everybody for your effort!

If you want to have a better understand why NumberXaero method works is because you are not accumulating the rotation over time. The reason why you get this weird tilt is because i bet you were doing rotation*=currentRotation, so the tilt you were getting is correct. By rebuilding the rotation matrix you change the order in wich the rotation are applied which in terms give you the correct orientation.



#12 BlackJoker   Members   -  Reputation: 556

Like
0Likes
Like

Posted 23 September 2014 - 05:54 AM

Interesting... will it not brake camera rotation aroud itself? I know that just changing the multiplication order also gives this effect, but it brake camera rotation on 360 degrees and make it rotation not arount itself, but around "north and south" poles and also flipping on 180 degrees on the each pole.


Edited by BlackJoker, 23 September 2014 - 05:54 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS