10

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

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

4

5 回答 5

8

解决方案:

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

using namespace glm;

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


解释:

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

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

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

于 2015-05-01T18:27:37.963 回答
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;
}

希望这可以帮助。

于 2014-12-14T01:21:22.773 回答
0

正在获得(或更好: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方向看,因此必须转动相机。现在,作为一个人,我会将其描述为 180 度y的旋转,但是 180 度的x旋转与另一个 180 度的旋转相结合z将产生相同的效果。

于 2014-12-14T19:41:33.210 回答
0

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

但这不是重复代码。出来的矩阵glm::lookat只是一个mat4. 经历从四元数到 3 个向量的转换,只有这样glm::lookat才能将其转换回方向只是浪费时间。你已经完成了 85%lookat的工作;剩下的就做吧。

于 2013-08-09T18:00:55.067 回答
0

当乘以LookAt 视图矩阵时,世界空间向量被旋转(带入)到相机的视图中,而相机的方向保持在原位

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

对于一个Camera对象,您需要获取其局部 forwardup方向向量才能计算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从它获得的 the 或 quat 不能直接用于此目的,因为它们描述的方向是相反的。

正确的旋转可以通过两种方式完成:

  • 计算lookAt矩阵的逆并将世界空间方向向量乘以这个旋转矩阵
  • (更有效)将 LookAt 矩阵转换为四元数并对其进行共轭而不是应用glm::inverse,因为结果是单位 quat 并且对于此类 quat,逆矩阵等于共轭。

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));
}
于 2018-08-23T14:05:43.940 回答