OpenGL:使用鼠标移动 2D 正交相机

OpenGL: Move 2D Orthographic Camera with Mouse

我正在使用 C++ 中的 OpenGL 为我的游戏制作关卡编辑器。我正在尝试像在 Unity 引擎 2D 场景相机中一样制作编辑器相机,但是当我尝试为相机实现鼠标移动(相机平移)时遇到问题。我正在将鼠标位置从屏幕转换为世界 space。

ScreenToWorldSpace方法:

Vector3 Application::ScreenToWorldSpace(int mousex, int mousey)
{
    double x = 2.0 * mousex / viewportWidth - 1;
    double y = 2.0 * mousey / viewportHeight - 1;

    Vector4 screenPos = Vector4(x, -y, -1.0f, 1.0f);

    Matrix4 ProjectionViewMatrix = camera1->GetProjectionMatrix() * camera1->GetViewMatrix();

    Matrix4 InverseProjectionViewMatrix = glm::inverse(ProjectionViewMatrix);

    Vector4 worldPos = InverseProjectionViewMatrix * screenPos;

    return Vector3(worldPos);
}

以上方法正确。

但我正在使用 ScreenToWorldSpace 坐标来更新相机位置。

渲染方法:

void Application::Render(float deltaTime)
{
    Vector3 pos = ScreenToWorldSpace(mousePosition.x, mousePosition.y);

    // This is the position of a tile not the camera
    position = Vector3(0, 0, 0);
    Vector3 rotation = Vector3(0, 0, 0);
    Vector3 scale = Vector3(1);

    Matrix4 translationMatrix = glm::translate(Matrix4(1.0f), position);
    Matrix4 rotationMatrix = glm::eulerAngleYXZ(rotation.y, rotation.x, rotation.z);
    Matrix4 scaleMatrix = glm::scale(Matrix4(1.0f), scale);

    modelMatrix = translationMatrix * rotationMatrix * scaleMatrix;

    if (mouseButtonDown)
    {
        Console << pos.x << ", " << pos.y << Endl;
        camera1->position = Vector3(pos.x, pos.y, -10);
    }

    {
        glScissor(0, 0, 900, 600);
        glEnable(GL_SCISSOR_TEST);
        glClearColor(236 / 255.0f, 64 / 255.0f, 122 / 255.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glViewport(0, 0, 900, 600);

        basicShader->Use();
        dirt_grass_tex->Use();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

        camera1->SetZoom(zoomFactor);
        camera1->Update();

        Matrix4 mvp = camera1->GetProjectionMatrix() * camera1->GetViewMatrix() * modelMatrix;

        basicShader->SetUniformMat4("MVP", mvp);

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glDisable(GL_SCISSOR_TEST);
    }

}

相机Class:

#include "camera.h"

Camera::Camera(int width, int height)
{
    swidth = width;
    sheight = height;
    position = Vector3(0, 0, -10);
    rotation = Vector3(0, 0, 0);

    m_direction = Vector3(0, 0, -5);
    m_up = Vector3(0, 1, 0);
    m_right = Vector3(1, 0, 0);

    m_offset = Vector3(-swidth / 2 * m_zoom, -sheight / 2 * m_zoom, 0);

    m_projection = glm::ortho(0.0f * m_zoom, (float)swidth * m_zoom, 0.0f * m_zoom, (float)sheight * m_zoom, -1000.0f, 0.0f);
}

Camera::~Camera()
{

}

void Camera::Update()
{

    Vector3 finalPos = position + m_offset;

    m_up = glm::cross(m_right, m_direction);

    m_viewMatrix = glm::lookAt(finalPos, finalPos + m_direction, m_up);

    m_viewMatrix = glm::scale(m_viewMatrix, Vector3(100));

}

void Camera::SetZoom(float zoom)
{
    m_zoom = zoom;
    m_offset = Vector3(-swidth / 2 * m_zoom, -sheight / 2 * m_zoom, 0);
    m_projection = glm::ortho(0.0f * m_zoom, (float)swidth * m_zoom, 0.0f * m_zoom, (float)sheight * m_zoom, -1000.0f, 0.0f);
}

以下是当我尝试将鼠标位置从屏幕转换为世界时移动相机时得到的输出 Space:

if (mouseButtonDown)
{
    Console << pos.x << ", " << pos.y << Endl;
    position = Vector3(pos.x, pos.y, 0);
}

但是,如果我使用 ScreenToWorldSpace 方法将鼠标位置从屏幕转换为世界 space,则对象会完美移动。看看下面的 gif:

以下是我要实现的目标:

所以我正在尝试制作 Game Engine Editor,因为我想实现 Editor Scene Camera 就像 unity / unreal engine scene camera。以下是我目前正在使用的编辑器:

我尝试查看不同的资源,但我一无所知。帮助我了解如何使用鼠标移动相机。

我认为正在发生的事情: 由于我使用相机的 projectionView 矩阵将鼠标位置从屏幕转换为世界 space 并使用这些世界坐标移动相机位置导致了问题,因为每当相机移动时,projectionView 都会更新,从而改变鼠标相对位置以递归方式查看矩阵。

我会很感激一些帮助。

通常,您不会希望将鼠标位置直接写入相机位置(因为这在实践中用途有限 - 无论何时单击屏幕,相机都会跳跃)。

您可能想按照这些思路做些什么:

Vector3 g_lastPosition;

void onMousePressed(int x, int y) {

  // record starting position!
  g_lastPosition  = ScreenToWorldSpace(x, y);
}

void onMouseMove(int x, int y) {

  // find the difference between new position, and last, in world space
  Vector3 new_pos = ScreenToWorldSpace(x, y);
  Vector3 offset = new_pos - g_lastPosition;
  g_lastPosition = new_pos;

  // now move camera by offset
  camera->position += offset
}

如果您处于正交视图中,那么您实际上根本不需要担心投影矩阵。

int g_lastX;
int g_lastY;

void onMousePressed(int x, int y) {

   // store mouse pos
   g_lastX = x;
   g_lastY = y;
}

void onMouseMove(int x, int y) {

   // find the difference between new position, and last, in pixels
   int offsetX = x - g_lastX;
   int offsetY = y - g_lastY;

   // update mouse pos
   g_lastX = x;
   g_lastY = y;

   // get as ratio +/- 1
   float dx = ((float) offsetX) / swidth;
   float dy = ((float) offsetY) / sheight;

   // now move camera by offset (might need to multiply by 2 here?)
   camera->position.x += camera->m_offset.x * dx;
   camera->position.y += camera->m_offset.y * dy;
}

但一般来说,对于任何基于鼠标的移动,您总是希望考虑添加偏移量,而不是设置精确位置。