Камера Arcball инвертируется при азимуте 90 градусов

Я пытаюсь реализовать камеру в стиле аркбола. Я использую glm :: lookAt, чтобы держать камеру направленной на цель, а затем перемещаю ее по поверхности сферы, используя углы азимута / наклона для поворота вида.

Я сталкиваюсь с проблемой, когда представление переворачивается вверх дном, когда азимут приближается к 90 градусам.

Вот соответствующий код:

  1. Получите проекцию и просмотрите martrices. Работает в основном цикле

    void Visual::updateModelViewProjection()
    {
    model = glm::mat4();
    projection = glm::mat4();
    view = glm::mat4();
    
    projection = glm::perspective
    (
    (float)glm::radians(camera.Zoom),
    (float)width / height, // aspect ratio
    0.1f, // near clipping plane
    10000.0f // far clipping plane
    );
    
    view = glm::lookAt(camera.Position, camera.Target, camera.Up);
    }
    
  2. Событие перемещения мыши, для вращения камеры

    void Visual::cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
    {
    if (leftMousePressed)
    {
    ...
    }
    
    if (rightMousePressed)
    {
    GLfloat xoffset = (xpos - cursorPrevX) / 4.0;
    GLfloat yoffset = (cursorPrevY - ypos) / 4.0;
    
    camera.inclination += yoffset;
    camera.azimuth -= xoffset;
    if (camera.inclination > 89.0f)
    camera.inclination = 89.0f;
    if (camera.inclination < 1.0f)
    camera.inclination = 1.0f;
    
    if (camera.azimuth > 359.0f)
    camera.azimuth = 359.0f;
    if (camera.azimuth < 1.0f)
    camera.azimuth = 1.0f;
    
    float radius = glm::distance(camera.Position, camera.Target);
    camera.Position[0] = camera.Target[0] + radius * cos(glm::radians(camera.azimuth)) * sin(glm::radians(camera.inclination));
    camera.Position[1] = camera.Target[1] + radius * sin(glm::radians(camera.azimuth)) * sin(glm::radians(camera.inclination));
    camera.Position[2] = camera.Target[2] + radius * cos(glm::radians(camera.inclination));
    
    camera.updateCameraVectors();
    }
    
    cursorPrevX = xpos;
    cursorPrevY = ypos;
    }
    
  3. Рассчитать векторы ориентации камеры

    void updateCameraVectors()
    {
    Front = glm::normalize(Target-Position);
    Right = glm::rotate(glm::normalize(glm::cross(Front, {0.0, 1.0, 0.0})), glm::radians(90.0f), Front);
    Up = glm::normalize(glm::cross(Front, Right));
    }
    

Я почти уверен, что это связано с тем, как я вычисляю правильный вектор моей камеры, но я не могу понять, как это компенсировать.

Кто-нибудь сталкивался с этим раньше? Какие-либо предложения?

2

Решение

Это распространенная ошибка в использовании lookAt для вращения камеры. Вы не должны. Направления назад / вправо / вверх являются столбцы вашей матрицы просмотра. Если у вас уже есть, то вам даже не нужно lookAt, который пытается повторить некоторые из ваших расчетов. С другой стороны, lookAt не поможет вам найти эти векторы в первую очередь.

Вместо этого сначала создайте матрицу представления как композицию переводов и вращений, а затем извлеките эти векторы из ее столбцов:

void Visual::cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
...
if (rightMousePressed)
{
GLfloat xoffset = (xpos - cursorPrevX) / 4.0;
GLfloat yoffset = (cursorPrevY - ypos) / 4.0;

camera.inclination = std::clamp(camera.inclination + yoffset, -90.f, 90.f);
camera.azimuth = fmodf(camera.azimuth + xoffset, 360.f);

view = glm::mat4();
view = glm::translate(view, glm::vec3(0.f, 0.f, camera.radius)); // add camera.radius to control the distance-from-target
view = glm::rotate(view, glm::radians(camera.inclination + 90.f), glm::vec3(1.f,0.f,0.f));
view = glm::rotate(view, glm::radians(camera.azimuth), glm::vec3(0.f,0.f,1.f));
view = glm::translate(view, camera.Target);

camera.Right = glm::column(view, 0);
camera.Up = glm::column(view, 1);
camera.Front = -glm::column(view, 2); // minus because OpenGL camera looks towards negative Z.
camera.Position = glm::column(view, 3);

view = glm::inverse(view);
}
...
}

Затем удалите код, который вычисляет вид и векторы направления из updateModelViewProjection а также updateCameraVectors,

Отказ от ответственности: этот код не проверен. Вам может понадобиться где-то исправить знак минус, порядок операций или соглашения могут не совпадать (Z вверх или Y вверх и т. Д.).

1

Другие решения

Других решений пока нет …