[Solved] Problem rolling / pitching camera

Hi all.

I am try to pitch (nodding motion) a CameraPersp using its orientation quaternion. This is how i am currently doing this:

CameraPersp pitchedCam = mCam;    // Copy base camera
float pitchAmount = 0.5f;
quat pitch = mCam.getOrientation();    // Get current camera orientation
vec3 pitchAxis = glm::rotate( pitch, vec3(1.0f, 0.0f, 0.0f));    // Get camera x axis
pitch = glm::rotate ( pitch, pitchAmount, pitchAxis );             // Roll around x axis
pitchedCam.setOrientation( pitch );

I thought this logic would be the same for roll and yaw (just by changing the relative axis) but it gives unexpected results in all cases (swinging the camera around in unspecified axes). Does this have something to do with the WorldUp value in CameraPersp? I feel i’m missing something really obvious.

Any solutions or alternatives for this would be greatly appreciated (preferably using the camera orientation rather than additional model / view matrices).


CameraPersp pitchedCam = cam;
float pitchAmount = std::sin ( app::getElapsedSeconds() ) * 0.5f;
quat pitch = cam.getOrientation() * glm::angleAxis( pitchAmount, vec3 ( 1.0f, 0.0f, 0.0f ) );
pitchedCam.setOrientation( pitch );

That what you’re after?


With the other DOFs:

CameraPersp pitchedCam = cam;
float pitchAmount = std::sin ( app::getElapsedSeconds() ) * 0.5f;
float yawAmount   = std::cos ( app::getElapsedSeconds() * 0.5f ) * 0.5f;
float rollAmount  = std::cos ( app::getElapsedSeconds() * 0.3f ) * 0.5f;
quat pitchYawRoll = cam.getOrientation() * glm::angleAxis( pitchAmount, vec3 ( 1.0f, 0.0f, 0.0f ) )
                                         * glm::angleAxis( yawAmount,   vec3 ( 0.0f, 1.0f, 0.0f ) )
                                         * glm::angleAxis( rollAmount,  vec3 ( 0.0f, 0.0f, 1.0f ) );
pitchedCam.setOrientation( pitchYawRoll );

Perhaps it’s related to your assumption that the pitch axis is equal to the world x-axis? You may want to retrieve the camera’s pitch axis using

vec3 pitchAxis, yawAxis;
mCam.getBillboardVectors( &pitchAxis, &yawAxis );

Thanks guys.

@lithium I will try that out, though I thought that quaternion rotations were added together not multiplied (is that incorrect)?

@paul.houx I wasnt assuming that as this line should have given me the axes relative to the camera no? The billboard vectors shortcut is useful though thanks.

vec3 pitchAxis = glm::rotate( mCam.getOrientation(), vec3(1.0f, 0.0f, 0.0f));

Ah yeah, I see. Still, the getBillboardVectors method is preferable, I think. Might both be faster and more accurate (edit: internally, getBillboardVectors also uses glm::rotate( mOrientation, vec3( 1, 0, 0 ) ), so accuracy is the same. However, since the resulting vector is then stored in the view matrix and this matrix is cached, calling getBillboardVectors may be faster if the camera does not change. If the camera does change, it will be slower. For performance reasons, you should stick to glm::rotate( mOrientation, vec3( 1, 0, 0 ) ) ).

Gosh, it’s been a few weeks since I programmed in C++ and somehow I’m having a hard time remembering how things worked (it will all come back to me, no worries), but the correct way of applying the rotation would be:

// Obtain pitch axis.
vec3 pitchAxis, yawAxis;
mCam.getBillboardVectors( &pitchAxis, &yawAxis );

// Create pitch quaternion.
auto pitch = glm::angleAxis( pitchAngleInRadians, pitchAxis );

// Obtain current orientation.
auto orientation = mCam.getOrientation();

// Modify current orientation by applying pitch quaternion.
mCam.setOrientation( orientation * pitch ); // might also be pitch * orientation

Quaternions should be multiplied for applying one after the other. You would multiply with the inverse of a quaternion to ‘undo’ its effect. It is easy to think of them as operators and not just 'value’s.

Thanks for all the suggestions, I was still getting strange results until i reordered the quaternions (as @paul.houx suspected). The calculation must be:

mCam.setOrientation( pitch * orientation );

(similar to matrix multiplication) and not the the other way round.