【问题标题】:Converting glm::lookat matrix to quaternion and back将 glm::lookat 矩阵转换为四元数并返回
【发布时间】:2013-08-09 16:55:13
【问题描述】:

我正在使用 glm 创建一个相机类,但我遇到了一些关于查看功能的问题。我使用四元数来表示旋转,但我想使用 glm 预先编写的查看函数来避免重复代码。这是我现在的查看功能:

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) 时,我的相机没有旋转到那个点。当我在查看调用后调用 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;
}

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

【问题讨论】:

  • 这是一篇旧帖子,我也遇到了同样的问题。根据research.ncl.ac.uk/game/mastersdegree/graphicsforgames/… 第 2 页和第 6 页,您应该使用 -position 并使用较新的 glm 编写 glm::mat4(1.0f) 标识。因此:对于 Camera::GetView() 的 glm::translate(glm::mat4(1.0f), -position)。在做 A * B glm 时我还没有理解的东西似乎在计算 B * A。
  • 我自己关于为什么 A * B 实际上在做 B * A。OpenGL 约定是 M * x,其中 M 是矩阵,其中 x 是列向量。在内部,他们使用转置矩阵(列主顺序),因此 (M * x)' 是 x' * M' 其中是 ' 转置运算符。 Afaik 他们仍然想遵循 M * x 约定,因此他们的 * 进行转置。

标签: c++ opengl quaternions glm-math


【解决方案1】:

解决方案:

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

using namespace glm;

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


解释:

lookAt 函数是gluLookAt 的替代品,用于构造view matrix

视图矩阵用于围绕观察者旋转世界,因此是相机变换的逆矩阵。

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

【讨论】:

  • 赞成,只是帮我将一个可怕的方向向量转换成一个合适的四元数
【解决方案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;
}

希望这会有所帮助。

【讨论】:

    【解决方案3】:

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

    但它不是重复代码。来自glm::lookat 的矩阵只是一个mat4。将四元数转换为 3 个向量,只是为了让glm::lookat 可以将其转换回一个方向,这只是浪费时间。你已经完成了lookat 85% 的工作;剩下的就做吧。

    【讨论】:

    • 这确实不是一个令人满意的答案,因为可能有理由实际持有一个四元数。当有人要求您提供一种生成lookat-quaternion的方法时,说“只使用矩阵”是没有回答的。
    • @opatut:我没有说“只使用矩阵”。我说过,当您可以直接使用四元数时,将四元数转换为 3 个查看向量的唯一目的是生成矩阵是没有意义的。
    【解决方案4】:

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

    当我在查看调用后调用 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 度将产生相同的效果。

    【讨论】:

      【解决方案5】:

      当乘以LookAt 视图矩阵时,世界空间向量被旋转(引入)到相机的视图中,同时 相机的方向保持不变。

      因此,相机实际向 右侧 旋转 45 度是通过一个矩阵实现的,该矩阵将向所有世界空间的 向左 旋转 45 度顶点。

      对于Camera 对象,您需要获取其本地 forwardup 方向向量,以便计算lookAt 视图矩阵。

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

      当使用四元数来存储对象的方向时(无论是相机还是其他任何东西),通常这个rotation quat 用于计算定义其局部空间的向量(左-在下面的例子中递了一个):

      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));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-06
        • 2012-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多