Вращение куба мышью с использованием OpenGL

Я использую OpenGL и GLUT для отображения куба. Теперь я хочу иметь возможность вращать камеру вокруг куба, перетаскивая мышь через окно. Когда мышь перемещается горизонтально, куб должен вращаться вокруг своей вертикальной оси, а когда мышь движется вертикально, куб должен вращаться вокруг своей горизонтальной оси, оба на фиксированном расстоянии. d,

Чтобы добиться этого, я попытался представить камеру в сферических координатах, theta а также phi, Когда мышь перемещается горизонтально по экрану, я увеличиваю (вправо) или уменьшаю (влево) thetaи когда мышь двигается вертикально, я увеличиваю (вверх) или уменьшаю (вниз) phi, Затем я использую следующие уравнения для определения декартовой позиции камеры и связанной с ней матрицы вида:

float eye_x = d * sin(phi) * cos(theta);
float eye_y = d  * sin(phi) * sin(theta);
float eye_z = d  * cos(phi);
glm::vec3 centre(0.0f, 0.0f, 0.0f);
glm::vec3 up(0, 1, 0);
view_matrix = glm::lookAt(eye, centre, up);

Когда я запускаю этот код, куб вращается, когда я перемещаю мышь вокруг окна, но не так, как я ожидал. Кроме того, куб, кажется, внезапно меняет свою ориентацию в определенных точках.

Может кто-нибудь указать мне правильное направление относительно того, как я должен правильно реализовать это?

Спасибо!

1 ответ

Упомянутый вами разрыв, "переворачивание", происходит от использования фиксированного направления "вверх" в вашем lookat матричная конструкция. Если направление "глаз" находится в направлении "вверх", lookat матрица ведет себя странно. Таким образом, ваш код должен был бы скорректировать прямую "вверх" соответствующим образом.

Но основная проблема не вращается так, как вы хотите, чтобы он вращался? Это происходит из-за использования неправильного инструмента для решения проблемы, а также из-за отсутствия полного понимания самой проблемы.

Я собираюсь предположить, что вы хотите реализовать что-то вроде управления вращением мыши в приложениях трехмерного моделирования, таких как Maya или Blender3D. Если вы обратите внимание, вы заметите, что ориентация зрителя действительно учитывается такими элементами управления мышью. Если вы измените угол, под которым вы смотрите на объект, вращение, которое вы применяете к этому объекту, изменится.

Или, другими словами, вы хотите контролировать ориентацию объекта относительно камеры. Но матрица, которую вы хотите окончательно установить, - это вращение относительно мира.

Итак, вот что вы делаете. Вам необходимо сгенерировать смещение ориентации на основе движений мыши. Это относительно камеры. Затем вам нужно преобразовать это смещение ориентации, чтобы оно было относительно мира. И затем, вы применяете это к текущей ориентации объекта.

Сферические координаты не могут этого сделать. Они имеют дело с углами, которые относятся к миру. И преобразовать такие углы сложно, если не невозможно. Вместо этого вам нужно работать с ориентациями напрямую, а не с углами.

Это означает или матрицы или кватернионы.

У неофициального GL SDK, который я написал, есть класс, который делает это. Ключевой фрагмент кода таков:

void ObjectPole::RotateViewDegrees( const glm::fquat &rot, bool bFromInitial )
{
    if(!m_bIsDragging)
        bFromInitial = false;

    if(m_pView)
    {
        glm::fquat viewQuat = glm::quat_cast(m_pView->CalcMatrix());
        glm::fquat invViewQuat = glm::conjugate(viewQuat);
        m_po.orientation = glm::normalize((invViewQuat * rot * viewQuat) *
            (bFromInitial ? m_startDragOrient : m_po.orientation));
    }
    else
        RotateWorldDegrees(rot, bFromInitial);
}

Входными данными для функции является кватернион, представляющий дельту вращения, вычисляемую на основе того, насколько мышь перемещалась между кадрами. Задача этой функции - применить эту дельту к текущей ориентации объекта. Тем не менее, дельта находится в пространстве камеры (это пространство, которое видит пользователь). Поскольку ориентация хранимого объекта находится в мировом пространстве, нам необходимо преобразовать дельту в мировое пространство, прежде чем применять ее к ориентации объекта.

Это работа invViewQuat * rot * viewQuat, Причина, по которой эта математика работает, немного сложна.

Другие вопросы по тегам