使用 GLM 正确旋转 Open GL 相机

Posted

技术标签:

【中文标题】使用 GLM 正确旋转 Open GL 相机【英文标题】:Rotating a Open GL camera correctly using GLM 【发布时间】:2012-08-27 08:40:43 【问题描述】:

我有一个相机类,它是这样初始化的:

CameraFP::CameraFP()  
    this->aspect_ratio = 800.0f / 600.0f;
    this->fov = 45.0f;
    this->near_plane = 0.1f;
    this->far_plane = 1000.0f;
    this->position = glm::vec3(0, 0, 0);
    this->target = position + glm::vec3(0, 0, -1);
    this->up = glm::vec3(0, 1, 0);
    this->m_rotation = glm::mat4(1.0);

    m_view = glm::lookAt(position, target, up);
    m_projection = glm::perspective(fov, aspect_ratio, near_plane, far_plane);

还有其他导入功能:

void CameraFP::update(sf::Window *app)  
    process_keyboard(app);
    process_mouse(app);

    calculate_view();


void CameraFP::process_keyboard(sf::Window *app)  
    const sf::Input *input = &app->GetInput();

    up = m_rotation * glm::vec3(0, 1, 0);

    glm::vec3 forward = glm::vec3(0, 0, -1);
    glm::vec3 forward_rotated = m_rotation * forward;

    glm::vec3 right = glm::vec3(1, 0, 0);
    glm::vec3 right_rotated = m_rotation * right;

    if (input->IsKeyDown(sf::Key::W))  
        position += forward_rotated;
    
    if (input->IsKeyDown(sf::Key::S))  
        position -= forward_rotated;
    
    if (input->IsKeyDown(sf::Key::A))  
        position -= right_rotated;
    
    if (input->IsKeyDown(sf::Key::D))  
        position += right_rotated;
    


void CameraFP::process_mouse(sf::Window *app)  
    // TODO: Make the below constants, and take framerate into account
    GLfloat SPEED_X = 0.000001f;
    GLfloat SPEED_Y = 0.000001f;

    GLfloat mouse_x = app->GetInput().GetMouseX();
    GLfloat mouse_y = app->GetInput().GetMouseY();

    GLfloat mouse_x_delta = old_mouse_x - mouse_x;
    GLfloat mouse_y_delta = old_mouse_y - mouse_y;

    if (mouse_x_delta != 0 ||
        mouse_y_delta != 0)  
        if (mouse_x_delta != 0)  
            y_rot += mouse_x_delta * SPEED_X;

            m_rotation = glm::rotate(m_rotation, y_rot, glm::vec3(0, 1, 0));
        
        if (mouse_y_delta != 0)  
            x_rot += mouse_y_delta * SPEED_Y;

            m_rotation = glm::rotate(m_rotation, x_rot, glm::vec3(1, 0, 0));;
        
    

    this->old_mouse_x = mouse_x;
    this->old_mouse_y = mouse_y;

    app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);



void CameraFP::calculate_view()  
    glm::vec3 forward = glm::vec3(0, 0, -1);
    glm::vec3 forward_rotated = m_rotation * forward;

    target = position += glm::normalize(forward_rotated);

    m_view = glm::lookAt(position, target, up);

我的问题是,当我编译项目时,编译器输出一个错误说:

\CameraFP.cpp|59|error: no match for 'operator*' in '((CameraFP*)this)->CameraFP::m_rotation * glm::detail::tvec3<float>(((const int&)((const int*)(&0))), ((const int&)((const int*)(&1))), ((const int&)((const int*)(&0))))'|

据我了解 vec = mat4 * vec 应该产生一个旋转向量?由于无法测试这段代码,不知道功能是否正常。

编辑

根据 cmets 和 awnsers 更新了代码。我现在的问题是我在渲染函数的某个地方得到了一个 BSOD...

void CameraFP::process_keyboard(sf::Window *app)  
    const sf::Input *input = &app->GetInput();

    up = m_rotation * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f);

    glm::vec4 forward = glm::vec4(0.0f, 0.0f, -1.0f, 0.0f);
    glm::vec4 forward_rotated = m_rotation * forward;

    glm::vec4 right = glm::vec4(1.0f, 0.0f, 0.0f, 0.0f);
    glm::vec4 right_rotated = m_rotation * right;

    if (input->IsKeyDown(sf::Key::W))  
        position += forward_rotated;
    
    if (input->IsKeyDown(sf::Key::S))  
        position -= forward_rotated;
    
    if (input->IsKeyDown(sf::Key::A))  
        position -= right_rotated;
    
    if (input->IsKeyDown(sf::Key::D))  
        position += right_rotated;
    


void CameraFP::process_mouse(sf::Window *app)  
    // TODO: Make the below constants, and take framerate into account
    GLfloat SPEED_X = 0.000001f;
    GLfloat SPEED_Y = 0.000001f;

    GLfloat mouse_x = app->GetInput().GetMouseX();
    GLfloat mouse_y = app->GetInput().GetMouseY();

    GLfloat mouse_x_delta = old_mouse_x - mouse_x;
    GLfloat mouse_y_delta = old_mouse_y - mouse_y;

    if (mouse_x_delta != 0 ||
        mouse_y_delta != 0)  
        if (mouse_x_delta != 0)  
            y_rot += mouse_x_delta * SPEED_X;

            m_rotation = glm::rotate(m_rotation, y_rot, glm::vec3(0.0f, 1.0f, 0.0f));
        
        if (mouse_y_delta != 0)  
            x_rot += mouse_y_delta * SPEED_Y;

            m_rotation = glm::rotate(m_rotation, x_rot, glm::vec3(1.0f, 0.0f, 0.0f));;
        
    

    this->old_mouse_x = mouse_x;
    this->old_mouse_y = mouse_y;

    app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);


void CameraFP::calculate_view()  
    glm::vec4 forward = glm::vec4(0.0f, 0.0f, -1.0f, 0.0f);
    glm::vec4 forward_rotated = m_rotation * forward;

    target = position += forward_rotated;

    m_view = glm::lookAt(v4tov3(position), v4tov3(target), v4tov3(up));


glm::vec3 v4tov3(glm::vec4 v1)  
    return glm::vec3(v1.x, v1.y, v1.z);

编辑 2

现在的问题是用鼠标旋转相机,它只是不起作用,由于某种原因,x 轴上的变化经常影响 y 轴上的变化,反之亦然。此外,如果我在 x 轴(y 旋转)上向右或向左移动鼠标,相机将向左旋转...

void CameraFP::process_mouse(sf::Clock *clock, sf::Window *app)  
    // TODO: Make the below constants, and take framerate into account
    GLfloat SPEED_X = 0.25f;
    GLfloat SPEED_Y = 0.25f;

    GLfloat screen_x = app->GetWidth();
    GLfloat screen_y = app->GetHeight();

    GLfloat mouse_x = float(screen_x / 2 - app->GetInput().GetMouseX());
    GLfloat mouse_y = float(screen_y / 2 - app->GetInput().GetMouseY());

    GLfloat mouse_x_delta = old_mouse_x - mouse_x;
    GLfloat mouse_y_delta = old_mouse_y - mouse_y;

    GLfloat current_time = clock->GetElapsedTime();
    GLfloat delta_time = current_time - last_time;

    this->last_time = current_time;

    if (mouse_x_delta != 0 ||
        mouse_y_delta != 0)  
        if (mouse_x_delta != 0)  
            y_rot += glm::radians(delta_time * SPEED_X * mouse_x);

            m_rotation = glm::rotate(m_rotation, y_rot, glm::vec3(0.0f, 1.0f, 0.0f));

            std::cout << "Y Rotation: " << y_rot << "\n";
        
        if (mouse_y_delta != 0)  
            x_rot += glm::radians(delta_time * SPEED_Y * mouse_y);

            m_rotation = glm::rotate(m_rotation, x_rot, glm::vec3(1.0f, 0.0f, 0.0f));

            std::cout << "X rotation: " << x_rot << "\n";
        
    

    app->SetCursorPosition(screen_x / 2, screen_y / 2);

    this->old_mouse_x = float(screen_x / 2 - app->GetInput().GetMouseX());
    this->old_mouse_y = float(screen_y / 2 - app->GetInput().GetMouseY());

【问题讨论】:

未找到 mat4*vec3 的运算符 *。 m_rotationmat4forwardvec3。我认为应该是vec4 @AquilaRapax 但是,我不是打算将位置表示为 vec3s 吗?我认为我不应该一直在 glm::vec3 和 glm::vec4 之间进行转换。我应该使用 glm::mat3 来表示我的旋转吗? 通常使用齐次坐标,因此所有向量和矩阵的维度都是 4。对于向量,最后一个坐标称为 w-coordinate 并且必须为 1。所以你不需要将 vec3 转换为 vec4。只需使用 vec4 并将第 4 个坐标设置为 1。有关更多信息,请查看 en.wikipedia.org/wiki/Homogeneous_coordinates 【参考方案1】:

替换所有 glm::vec3(0, 1, 0);通过 glm::vec3(0.0f, 1.0f, 0.0f);

至于 vec-mac 乘法,AquilaRapax 是正确的,因为您只能将 mat4 与 vec4 相乘。但是由于您要乘以方向,因此第 4 个坐标应该是 0.0f,而不是 1.0f。这将产生忽略翻译的效果(1.0 会将它们考虑在内,这是您不想要的)

有关矩阵的详细信息,请参阅http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/。

但是,保留 vec3 而不是 vec4 通常是个好主意,主要是为了清楚起见(即,使用 glm::vec3 mPosition 而不是 glm::vec4 mPosition)。因此,拥有 2 个这样的功能(未经测试)很方便:

glm::vec3 TransformDirection(glm::vec3 pDirection, glm::mat4 pMatrix)
    return pMatrix * glm::vec4(pDirection, 0.0f);


glm::vec3 TransformPosition(glm::vec3 pDirection, glm::mat4 pMatrix)
    return pMatrix * glm::vec4(pDirection, 1.0f);

【讨论】:

@Calvin1602 所以我必须将我的位置(以及我所有的其他向量)设为 vec4?例如,由于我需要通过旋转矩阵变换前向向量,因此我需要将旋转后的向量应用于位置。对于方向,我必须将 0.0f 作为最后一个参数,对于位置和目标,1.0f? @Calvin1602 例如。在 calculate_view 函数中,我将不得不这样做:pastebin.com/6F0gh8dG ...我的计算机在运行时崩溃:| @Calvin1602 哇!它不会给我蓝屏!谢谢...我现在的问题似乎是鼠标,无论我在x轴上向哪个方向移动鼠标我总是向右转,无论我在y轴上向哪个方向移动鼠标,它总是向下看.. . 嗯... 查看编辑。对于鼠标: old_mouse 是错误的。将其完全删除并改用尺寸/2。或者删除 SetCursorPosition()。 @Calvin1602 好的,完成了。我现在更加困惑,因为当我向左移动鼠标时,它会向右移动!当我向右移动时,它会向下移动相机……我真的很困惑……pastebin.com/PyVYC9X1【参考方案2】:

process::mouse 的末尾,您将坐标保存在old_mouse_xold_mouse_y 中,然后将光标移动到屏幕中间。如果您这样做,old_mouse_xold_mouse_y 将无效。您需要做的是在重新定位光标后设置这些变量:

app->SetCursorPosition(app->GetWidth() / 2, app->GetHeight() / 2);
this->old_mouse_x = app->GetWidth() / 2;
this->old_mouse_y = app->GetHeight() / 2;

【讨论】:

以上是关于使用 GLM 正确旋转 Open GL 相机的主要内容,如果未能解决你的问题,请参考以下文章

GL Math : GLM 旋转函数旋转整个坐标系

相机 glm / OpenGL 即时模式的示例

如何旋转 glm 相机

绕轴旋转相机?

如何使用 glm::rotate 围绕原点以外的点旋转对象?

OpenGL/GLSL/GLM - Skybox 像第三人称一样旋转