我正在使用 glm 创建一个相机类,并且我在使用 Lookat 函数时遇到了一些问题。我使用四元数来表示旋转,但我想使用 glm 预先编写的 Lookat 函数来避免重复代码。这是我现在的查看功能:
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);
}
但是,当我调用
LookAt(0.0f,0.0f,0.0f)
时,我的相机没有旋转到该点。当我在lookat调用后调用glm::eulerangles(rotation)
时,我得到一个具有以下值的vec3:(180.0f,0.0f,180.0f)。 position
是 (0.0f,0.0f,-10.0f),所以我根本不应该进行任何旋转来查看 0,0,0。这是构建视图矩阵的函数:
glm::mat4 Camera::GetView() {
view = glm::toMat4(rotation) * glm::translate(glm::mat4(), position);
return view;
}
为什么我没有得到正确的四元数,如何修复我的代码?
我遇到了类似的事情,简短的答案是你的lookMat可能需要反转/转置,因为它是相机旋转(至少在我的情况下),而不是世界旋转。 旋转世界将是相机旋转的逆过程。
我有一个 m_current_quat ,它是一个存储当前相机旋转的四元数。 我通过打印出 glm::lookAt 生成的矩阵并与通过应用 m_current_quat 和 m_camera_position 翻译得到的结果矩阵进行比较来调试该问题。 这是我测试的相关代码。
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;
}
希望这有帮助。
我想使用glm预先编写的lookat函数来避免重复代码。
但它不是重复代码。从
glm::lookat
出来的矩阵只是一个 mat4
。从四元数到 3 个向量的转换,只是为了 glm::lookat
可以将其转换回方向,这只是浪费时间。您已经完成了 lookat
85% 的工作;剩下的就做吧。
您正在获得(或更好:a)正确的旋转。
当我在lookat调用后调用
时,我会得到一个glm::eulerangles(rotation)
具有以下值:(180.0f、0.0f、180.0f)。vec3
是 (0.0f,0.0f,-10.0f),所以我根本不应该有任何旋转来查看 在 0,0,0。position
glm 遵循旧的固定功能 GL 的约定。在那里,眼睛空间被定义为放置在原点的相机,
x
指向右侧,y
向上并朝-z
方向看。由于您想朝正z
方向看,因此相机必须转动。现在,作为人类,我会将其描述为围绕 y
旋转 180 度,但围绕 x
旋转 180 度与围绕 z
旋转 180 度相结合将产生相同的效果。
当乘以
LookAt
视图矩阵时,世界空间向量将旋转(带到)到相机的视图中,同时相机的方向保持不变。
因此,相机实际向右旋转 45 度是通过一个矩阵实现的,该矩阵将向向左 45 度旋转应用于所有世界空间顶点。
对于Camera
对象,您需要获取其local
forward
和
up
方向向量,以便计算
lookAt
视图矩阵。
viewMatrix = glm::lookAtLH (position, position + camera_forward, camera_up);
当使用四元数存储对象(无论是相机还是其他任何物体)的方向时,通常使用此
rotation
四元数来计算定义其局部空间 的向量(下例中为左手向量) ):
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
因此,世界空间方向应向右
旋转 45 度,以反映相机的正确方向。 这就是为什么
lookMat
或从中获得的quat不能直接用于此目的,因为它们描述的方向是相反的。
正确的旋转可以通过两种方式完成:
计算
lookAt
glm::inverse
,因为结果是单位四元数,对于此类四元数,其倒数等于共轭。
LookAt
应如下所示:
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));
}