0
votes

EDIT: I've uploaded a video to youtube that shows what's happening now. When I rotate 60 degrees (pitch or yaw) then I get strange shaking. As you can see, the mouse input is working fine (no unwanted roll, and pitch/yaw occurs locally after a roll)

I'm trying to implement a flight simulator camera in an OpenGL game I'm making. I've followed what is mentioned at the bottom of this page and my resulting computation of the view matrix is:

my = my * 0.001f; //per-frame delta
mx = mx * 0.001f; //per-frame delta

glm::vec3 pitchAxis = glm::normalize( glm::cross( cameraDirection, cameraUp ) );
glm::vec3 yawAxis = glm::normalize( cameraUp );

glm::quat pitchQuat = glm::angleAxis( (float)my, pitchAxis );
glm::quat yawQuat = glm::angleAxis( (float)mx, yawAxis );

glm::quat comp = yawQuat * pitchQuat;

cameraDirection = cameraDirection * comp;

pitchAxis = glm::cross( cameraDirection, cameraUp );
cameraUp = glm::cross( pitchAxis, cameraDirection );

return glm::lookAt( cameraPosition, cameraPosition + cameraDirection, cameraUp );

where I calculate an incremental pitch/yaw quaternion based on mouse input and then apply that to the camera's direction. Then I re-calculate the camera's Up vector based on a new pitch axis using the cross-product.

When I test it I don't get any camera locks at 90 degrees but if I pitch and yaw then the camera also starts to roll (not wanted).

The accepted answer in this post suggests to use global pitch/yaw/roll value, not just the delta values I used above. So I went ahead and tried to do that:

//using left-handed coordinate system
glm::vec3 yawAxis = glm::vec3( 0.0f, 1.0f, 0.0f );
glm::vec3 pitchAxis = glm::vec3( -1.0f, 0.0f, 0.0f );
//glm::vec3 rollAxis = glm::vec3( 0.0f, 0.0f, -1.0f );

glm::quat yawQuat = glm::angleAxis( yaw, yawAxis );
glm::quat pitchQuat = glm::angleAxis( pitch, pitchAxis );

glm::quat composite = yawQuat * pitchQuat;

cameraDirection = composite * glm::vec3( 0.0f, 0.0f, -1.0f );

glm::quat rollQuat = glm::angleAxis( roll, cameraDirection );
cameraUp = rollQuat * glm::vec3( 0.0f, 1.0f, 0.0f );

return glm::lookAt( cameraPosition, cameraPosition + cameraDirection, cameraUp );

where yaw and pitch are incremented each frame if the mouse moves. I can still look around nicely but if:

  • I pitch 90 degrees up or down and keep going it seems like the camera flips/inverts.
  • I roll first, the pitch/yaw is still about the global axes (which is expected from the code) instead of the local camera axes which I think it should be and that's why I tried the first method...

I think the first issue could be fixed with a case that flips the global pitchAxis when the pitch > 90 degrees or <-90 degrees. But I'm stuck with fixing the second issue as my intuition keeps trying to use the first method I tried.

Is there a fundamental flaw with how I'm going about implementing this camera or am I missing something obvious?

1

1 Answers

2
votes

It it usally tricky to get the 3D cameras do what you want. Because of the order in witch yo apply your changes has an impact on the next change. When you try different approaches it can be more easy to apply the changes of pitch, yaw and roll in separate steps, like this:

Edit: Adding info about cameras local space

  • Apply pitch to camera direction and camera up, using the cameras local space (forward vector is direction of camera, up vector is the cameras up)
  • Apply yaw to camera direction and camera up, using new local space
  • Apply roll to camera direction and camera up, using new local space
  • Now use the camera for your view matrix

That way you can comment out the different steps when you are testing. When you are satisfied with the result, you can try to move the steps together for performance but the order of the changes can make it difficult.