将 glm::lookat 矩阵转换为四元数并返回

问题描述 投票:0回答:5

我正在使用 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;
}

为什么我没有得到正确的四元数,如何修复我的代码?

c++ opengl quaternions glm-math
5个回答
8
投票

解决方案:

您必须通过共轭来反转四元数的旋转:

using namespace glm;

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

**解释:**

lookAt 函数是 gluLookAt 的替代品,用于构造 视图矩阵

视图矩阵用于围绕观察者旋转世界,因此是相机变换的逆矩阵。 (例如,如果相机的变换向上移动,则世界在视图中向下移动)

通过取逆的逆,你可以得到实际的变换。


2
投票

我遇到了类似的事情,简短的答案是你的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;
}

希望这有帮助。


0
投票

我想使用glm预先编写的lookat函数来避免重复代码。

但它不是重复代码。从

glm::lookat
出来的矩阵只是一个
mat4
。从四元数到 3 个向量的转换,只是为了
glm::lookat
可以将其转换回方向,这只是浪费时间。您已经完成了
lookat
85% 的工作;剩下的就做吧。


0
投票

正在获得(或更好:a)正确的旋转。

当我在lookat调用后调用

glm::eulerangles(rotation)
时,我会得到一个
vec3
具有以下值:(180.0f、0.0f、180.0f)。
position
是 (0.0f,0.0f,-10.0f),所以我根本不应该有任何旋转来查看 在 0,0,0。

glm 遵循旧的固定功能 GL 的约定。在那里,眼睛空间被定义为放置在原点的相机,

x
指向右侧,
y
向上并朝
-z
方向看。由于您想朝正
z
方向看,因此相机必须转动。现在,作为人类,我会将其描述为围绕
y
旋转 180 度,但围绕
x
旋转 180 度与围绕
z
旋转 180 度相结合将产生相同的效果。


0
投票

当乘以

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
  • 矩阵的逆矩阵,并将世界空间方向向量乘以该旋转矩阵
  • (更高效)
  • 将 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)); }

	
© www.soinside.com 2019 - 2024. All rights reserved.