Rotating a cube with the mouse using OpenGL

I am using OpenGL and GLUT to display a cube. I now want to be able to rotate the camera around the cube by dragging the mouse across the window. When the mouse moves horizontally, the cube should rotate around its vertical axis, and when the mouse moves vertically, the cube should rotate around its horizontal axis, both at a fixed distance d.

To achieve this, I have tried representing the camera in spherical coordinates, theta and phi. When the mouse moves horizontally across the screen, I increase (right) or decrease (left) theta, and when the mouse moves vertically, I increase (up) or decrease (down) phi. Then, I use the following equations to determine the Cartesian position of the camera and the associated view matrix:

float eye_x = d * sin(phi) * cos(theta);
float eye_y = d  * sin(phi) * sin(theta);
float eye_z = d  * cos(phi);
glm::vec3 centre(0.0f, 0.0f, 0.0f);
glm::vec3 up(0, 1, 0);
view_matrix = glm::lookAt(eye, centre, up);

When I run this code, the cube does rotate as I move the mouse around the window, but not in the way I would expect. Also, the cube seems to “flip” its orientation suddenly at certain points.

Can somebody point me in the right direction as to how I should properly implement this?


Personally, I think rotating the cube should change the model matrix rather than the camera matrix, but let’s work with this for now. The reason why the cube seems to flip orientation is because up is always defined to be “up” in world space. As a result, if your camera turns upside down, your “up” will mandate the camera be flipped right side up. That transition is why the orientation flips. Another issue is that the rotation is relative to world space, rather than the camera. As a result, the rotation would seem arbitrary to the camera, but this is because of the camera’s current orientation. To do this properly using the camera matrix, one may wish to calculate the “up” using your rotation. This is pretty simple, as “up” is simply the cross between your camera position to target and the rotational railing on the x-z plane, something like glm::cross(-eye, vec3(sin(theta), -cos(theta), 0.0f)), where -eye is the vector towards the center and vec3(sin(theta), -cos(theta), 0.0f) is the vector along your rotation.

A far simpler way to do this is to just do the calculation in model space.

This is accomplished by doing something like

view_matrix = glm::lookAt(glm::vec3{0, 0, d}, glm::vec3{0, 0, 0}, glm::vec3{0, 1, 0}) * glm::rotate(glm::vec3(1, 0, 0), phi) * glm::rotate(glm::vec3(0, 0, 1), theta); or something like that. That being said, again I recommend using a model matrix instead.