Convert glm :: lookat matrix to quaternion and vice versa

I use glm to create a camera class and am having problems with the lookat function. I use quaternion to represent rotation, but I want to use the pre-written glm function to prevent code duplication. This is my lookat function right now:

void Camera::LookAt(float x, float y, float z) { glm::mat4 lookMat = glm::lookAt(position, glm::vec3(x, y, z), glm::vec3(0, 1, 0)); rotation = glm::toQuat(lookMat); } 

However, when I call LookAt(0.0f,0.0f,0.0f) , my camera does not rotate to this point. When I call glm::eulerangles(rotation) after calling lookat, I get vec3 with the following values: (180.0f, 0.0f, 180.0f). position is (0.0f, 0.0f, -10.0f), so I shouldn't have any rotation at all to watch 0,0,0. This is a function that builds a matrix of the form:

 glm::mat4 Camera::GetView() { view = glm::toMat4(rotation) * glm::translate(glm::mat4(), position); return view; } 

Why am I not getting the correct quaternion, and how can I fix my code?

+8
c ++ opengl quaternions glm-math
source share
5 answers

Decision:

You must invert the rotation of the quaternion by pairing it:

 using namespace glm; quat orientation = conjugate(toQuat(lookAt(vecA, vecB, up))); 


Explanation:

The lookAt function is a replacement for gluLookAt , which is used to build the view matrix .

The view matrix is ​​used to rotate the world around the viewer and, therefore, is the inverse of the camera conversion.

By accepting the inverse, you can get the actual conversion.

+6
source share

I came across something similar, the short answer is your look. Perhaps you need to flip / transpose, because this is the rotation of the camera (at least in my case), in contrast to the world rotation. The rotation of the world will be the reverse of the rotation of the camera.

I have m_current_quat, which is a quaternion that stores the current rotation of the camera. I debugged the problem by printing out the matrix created by glm :: lookAt, and comparing with the resulting matrix, which I get by applying m_current_quat and translating by m_camera_position. Here is the appropriate code for my test.

 void PrintMatrix(const GLfloat m[16], const string &str) { printf("%s:\n", str.c_str()); for (int i=0; i<4; i++) { printf("["); //for (int j=i*4+0; j<i*4+4; j++) // row major, 0, 1, 2, 3 for (int j=i+0; j<16; j+=4) // OpenGL is column major by default, 0, 4, 8, 12 { //printf("%d, ", j); // print matrix index printf("%.2f, ", m[j]); } printf("]\n"); } printf("\n"); } void CameraQuaternion::SetLookAt(glm::vec3 look_at) { m_camera_look_at = look_at; // update the initial camera direction and up //m_initial_camera_direction = glm::normalize(m_camera_look_at - m_camera_position); //glm::vec3 initial_right_vector = glm::cross(m_initial_camera_direction, glm::vec3(0, 1, 0)); //m_initial_camera_up = glm::cross(initial_right_vector, m_initial_camera_direction); m_camera_direction = glm::normalize(m_camera_look_at - m_camera_position); glm::vec3 right_vector = glm::cross(m_camera_direction, glm::vec3(0, 1, 0)); m_camera_up = glm::cross(right_vector, m_camera_direction); glm::mat4 lookat_matrix = glm::lookAt(m_camera_position, m_camera_look_at, m_camera_up); // Note: m_current_quat quat stores the camera rotation with respect to the camera space // The lookat_matrix produces a transformation for world space, where we rotate the world // with the camera at the origin // Our m_current_quat need to be an inverse, which is accompolished by transposing the lookat_matrix // since the rotation matrix is orthonormal. m_current_quat = glm::toQuat(glm::transpose(lookat_matrix)); // Testing: Make sure our model view matrix after gluLookAt, glmLookAt, and m_current_quat agrees GLfloat current_model_view_matrix[16]; //Test 1: gluLookAt gluLookAt(m_camera_position.x, m_camera_position.y, m_camera_position.z, m_camera_look_at.x, m_camera_look_at.y, m_camera_look_at.z, m_camera_up.x, m_camera_up.y, m_camera_up.z); glGetFloatv(GL_MODELVIEW_MATRIX, current_model_view_matrix); PrintMatrix(current_model_view_matrix, "Model view after gluLookAt"); //Test 2: glm::lookAt lookat_matrix = glm::lookAt(m_camera_position, m_camera_look_at, m_camera_up); PrintMatrix(glm::value_ptr(lookat_matrix), "Model view after glm::lookAt"); //Test 3: m_current_quat glLoadIdentity(); glMultMatrixf( glm::value_ptr( glm::transpose(glm::mat4_cast(m_current_quat))) ); glTranslatef(-m_camera_position.x, -m_camera_position.y, -m_camera_position.z); glGetFloatv(GL_MODELVIEW_MATRIX, current_model_view_matrix); PrintMatrix(current_model_view_matrix, "Model view after quaternion transform"); return; } 

Hope this helps.

+1
source share

I want to use the pre-written glm function to prevent code duplication.

But this is not duplicate code. The matrix that exits glm::lookat is just mat4 . Going through the conversion from quaternion to 3 vectors so that glm::lookat can convert it back to orientation is just a waste of time. You have already completed 85% of the lookat task; just do the rest.

0
source share

You get the right spin (or better: a).

When I call glm::eulerangles(rotation) after calling lookat, I get vec3 with the following values: (180.0f, 0.0f, 180.0f). position (0.0f, 0.0f, -10.0f), so I shouldn't have a rotation at all to watch at 0,0,0.

glm follows the conventions of the old fixed GL function. And there, the space for the eyes was defined as a camera located at the origin, with x to the right, y up and looking in the -z direction. Since you want to look in the positive z direction, the camera should turn. Now, as a person, I would describe it as a 180 degree rotation around y , but a 180 degree rotation around x in combination with another 180 degree rotation around z will have the same effect.

0
source share

When multiplied by a LookAt matrix LookAt vectors of world space are rotated ( reduced ) to the camera view when the camera orientation is maintained in place.

Thus, the actual camera rotation of 45 degrees to the right is achieved using a matrix that applies a rotation of 45 degrees to the left to all the vertices of the world.

For the Camera object, you will need to get your local forward and up direction vectors in order to compute lookAt type.

 viewMatrix = glm::lookAtLH (position, position + camera_forward, camera_up); 

When using quaternions to preserve the orientation of an object (whether it be a camera or something else), this rotation quator is usually used to calculate vectors that define its local space (left in the example below):

 glm::vec3 camera_forward = rotation * glm::vec3(0,0,1); // +Z is forward direction glm::vec3 camera_right = rotation * glm::vec3(1,0,0); // +X is right direction glm::vec3 camera_up = rotation * glm::vec3(0,1,0); // +Y is up direction 

Thus, the directions of world space should be rotated to the right to reflect the correct orientation of the camera.

This is why lookMat or lookMat derived from it cannot be directly used for this purpose, since the orientation that they describe is the opposite.

Proper rotation can be done in two ways:

  • Calculate the inverse matrix lookAt and lookAt vectors of the direction of world space on this rotation matrix
  • (more efficiently) Convert the LookAt matrix to a quaternion and glm::inverse it instead of applying glm::inverse , since the result is a single quat and for such quats the opposite is conjugate.

Your LookAt should look like this:

 void Camera::LookAt(float x, float y, float z) { glm::mat4 lookMat = glm::lookAt(position, glm::vec3(x, y, z), glm::vec3(0, 1, 0)); rotation = glm::conjugate( glm::quat_cast(lookMat)); } 
0
source share

All Articles