使用GLM正确旋转Open GL相机

6

我有一个相机类,它的初始化方式如下:

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

这里介绍一些导入(import)的其他功能:

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 应该会产生一个旋转后的向量?由于我还没有测试过这段代码,所以不知道这个函数是否能正确工作。

编辑

根据评论和答案更新了代码。现在我的问题是,在渲染函数中出现了蓝屏死机...

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

mat4vec3的运算符未找到。m_rotation是一个mat4,而forward是一个vec3。我认为它应该是一个vec4 - AquilaRapax
@AquilaRapax 但是,我不应该将位置表示为vec3吗?我认为我不应该一直在glm :: vec3和glm :: vec4之间进行转换。我应该使用glm :: mat3来表示我的旋转吗? - Darestium
通常使用齐次坐标,因此所有向量和矩阵的维数都为4。对于向量,最后一个坐标称为“w”坐标,必须为1。因此,您不需要将vec3转换为vec4。只需使用vec4并将第四个坐标设置为1即可。有关更多信息,请参见http://en.wikipedia.org/wiki/Homogeneous_coordinates。 - AquilaRapax
2个回答

8

将所有的glm::vec3(0, 1, 0)替换为glm::vec3(0.0f, 1.0f, 0.0f)。

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

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

然而,通常最好保留vec3而不是vec4,主要是为了清晰起见(即glm::vec3 mPosition而不是glm::vec4 mPosition)。因此,有两个这样的函数很方便(未经测试):

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。 - Darestium
@Calvin1602 哇!它没有给我蓝屏!谢谢...现在我的问题似乎是鼠标,无论我在x轴上如何移动鼠标,我总是向右转,而无论我在y轴上如何移动鼠标,它总是向下看...嗯... - Darestium
对于鼠标:old_mouse存在问题。完全删除它并使用dimensions/2代替。或者删除SetCursorPosition()。 - Calvin1602
很酷,谢谢链接!代码可以运行,但是我不太明白它的具体实现方式... - Darestium
对应教程:http://www.opengl-tutorial.org/beginners-tutorials/tutorial-6-keyboard-and-mouse/ - Calvin1602
显示剩余2条评论

0
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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接