Как я могу вращаться вокруг камеры относительно ее целевой точки?


18

Я рисую сцену, где камера свободно перемещается по вселенной. Класс камеры отслеживает точку обзора (или просмотра ), положение камеры и вектор вверх. Эти векторы / точки затем передаются в gluLookAt.

Панорамирование и масштабирование почти тривиальны для реализации. Тем не менее, я нахожу поворот вокруг взгляда на точку гораздо более серьезной проблемой. Я хочу написать функцию Camera.rotate, которая принимает 2 угла, один, который вращается вверх / вниз, и другой, который вращается влево / вправо вдоль воображаемой сферы, которая сосредоточена вокруг взгляда на точку.

Есть простой способ сделать это?

Я (кратко) прочитал о кватернионах, но я хотел посмотреть, было ли более простое решение, учитывая относительно простую конструкцию моей сцены.


У вас есть что-нибудь в вашем наборе инструментов, чтобы повернуть точку вокруг начала координат, учитывая ось и угол?
Сэм Хочевар

Ничего, кроме моего свободного понимания кватернионов, нет. Чем больше я смотрю на это, я думаю, что Quaternions может быть ответом. Однако я не уверен, как рассчитать значения осей x, y и z для использования в формулах кватернионов.
Люк

Я не скажу вам, что кватернионы - это не путь. Это очень хорошее решение вашей проблемы. Но вам понадобится кватернионный класс и способы взаимодействия с GL и GLU, и я чувствую, что вы должны сначала попытаться ознакомиться с матрицами преобразования. Другие могут не согласиться.
Сэм Хоцевар

подумайте, в каком порядке вы выполняете вращения. Применение вращений для перемещения в мировое пространство или для перемещения в пространство камеры отличается
Чарли

Ответы:


25

То, что вы просите, называется ротацией Arcball. Кватернионы - это простое решение, только если вы понимаете, как они работают. Однако вы можете достичь того же самого без кватернионов.

Предпосылки

Вы знаете, как вращать объекты в целом? Допустим, у вас есть объект в начале координат. Знаете ли вы, как вы поворачиваете его (подсказка: умножьте на некоторую матрицу вращения)? Если да, то я предполагаю, что вы знаете, что произойдет, если вы сначала переведите объект, а затем поверните его?

Вы должны знать, как рассчитать матрицу вращения по оси угла (просто, как круговая диаграмма, посмотрите на множество онлайн-уравнений, многие из них также дают вам код)

Решение

  • Получить камеры вверх и вправо векторов. Обратите внимание, что они должны быть нормализованы.
  • Получить вектор от точки фокусировки на камеру (camPosition - Focus). Это вектор, который вы собираетесь вращать. Давайте назовем это camFocusVector .
  • Решите, сколько вы хотите повернуть в рысканье / тангаже по отношению к камере
  • Создайте две матрицы вращения. Матрица первого поворота будет использовать вверх камеры как оси и поворот вокруг вертикальной оси под углом , что вы решили. Матрица второго поворота будет использовать право камеры в качестве оси и тангажа угла , который вы решили.
  • Теперь поверните camFocusVector с новыми матрицами вращения. Теперь это ваша новая позиция камеры относительно начала координат. Мы, конечно, хотим, чтобы это было относительно точки фокуса ...
  • Добавьте положение точки фокусировки в camFocusVector . Теперь это новая позиция вашей камеры. Переведите свою камеру соответственно.
  • Наконец, попросите камеру сфокусироваться на точке фокусировки, вызвав функцию lookAt ()

Предостережения

Вам придется следить за определенными случаями или особенностями, при которых ваша камера перестанет работать. Глядя прямо вниз / вверх, например. Я дам вам понять, как с этим справиться.

EDIT1: Как пересчитать ортонормированные векторы камеры

Вы уже знаете направление камеры ((cameraPos - focusPoint) .normalize ()). Теперь предположим, что у вашей камеры вверх + Y (или если текущая верхняя ось вашего мира ... это зависит от вас). Теперь просто пересечь направление с вверх , чтобы получить право . Выполнено? Нет! Ваш верхний вектор больше не является ортогональным к двум другим. Чтобы исправить это, перекрестное право с руководством , и вы получите ваш новый вверх .

Обратите внимание, что Грам-Шмидт действительно то, что следует использовать для ортонормирования векторов.

Опять же, обратите внимание на предостережения, так как в некоторых случаях это не сработает (например, направление параллельно вверх ).


Обратите внимание, что правый вектор можно получить с помощью normalize(up ^ camFocusVector)(или его противоположность, если левша).
Сам Хочевар

Выглядел хорошо до сих пор, прекрасно работал для вращения влево / вправо (у меня есть вектор вверх, так что это легко). Что означает «^» в вашем комментарии @SamHocevar? Это перекрестный продукт? Кроме того, как я могу пересчитать вектор повышения после выполнения переводов?
Люк

@Luke: Проверьте мой EDIT1
Самаурса

1
@Samaursa Большое спасибо. Ваше решение работает отлично, и я многому научился в процессе!
Люк

2
это ОТЛИЧНЫЙ ответ, большое спасибо за ваше объяснение. В моем случае я хотел, чтобы камера вращалась только вокруг целевой точки в плоскости XY, и поэтому после выполнения всех вычислений я всегда устанавливал cameraRight.z в 0, а затем вычислял вектор cameraUp. Это дало желаемый эффект. Просто подумал поделиться
codemonkey

1

Что вам нужно, это обычная камера ArcBall, которая в основном представляет собой камеру, которая сохраняет местоположение цели и позволяет вам перемещать камеру «сферически» вокруг этой цели.

Это в XNA, но IIRC я ​​использовал эту реализацию раньше, и она работала довольно хорошо:

http://roy-t.nl/index.php/2010/02/21/xna-simple-arcballcamera/


Эта реализация, кажется, движется только по правому вектору, который будет приближаться к небольшому значению величины . Он также перемещает точку LookAt, как то, куда я хочу переместить камеру (близко, но немного по-другому). Я считаю, что Самаурса предоставил самый простой и полный способ достижения этой цели.
Люк

Спасибо за ваш отзыв. Я должен признать, что я использовал эту реализацию немного как «черный ящик», но я не заметил никаких проблем. Чтобы уточнить, возможно, вы говорили о методах MoveCameraRight / MoveCameraForward? Эти методы были добавлены для перемещения камеры, но не являются частью интерфейса Arcball. Если вы хотите повернуть камеру вокруг цели, вы просто измените свойства Yaw или Pitch.
Дэвид Гувея

Извините, вы правы, это методы панорамирования. Я не заметил, как он установил грязный флаг для матрицы при изменении рыскания и высоты тона.
Люк

0

Вот мой код для поворота вокруг точки, и я часто использую его.

float distance;      // Straight line distance between the camera and look at point

// Calculate the camera position using the distance and angles
float camX = distance * -sinf(camAngleX*(M_PI/180)) * cosf((camAngleY)*(M_PI/180));
float camY = distance * -sinf((camAngleY)*(M_PI/180));
float camZ = -distance * cosf((camAngleX)*(M_PI/180)) * cosf((camAngleY)*(M_PI/180));

// Set the camera position and lookat point
gluLookAt(camX,camY,camZ,   // Camera position
          0.0, 0.0, 0.0,    // Look at point
          0.0, 1.0, 0.0);   // Up vector

1
Это в основном так, как я сделал это изначально. Есть много проблем с этим (по крайней мере, для меня). В основном эта реализация вращает только около 2 фиксированных осей. Допустим, вы поворачиваете вверх почти до северного полюса . Затем поверните направо . Вы обнаружите, что вращаетесь по крошечному кругу, а не следите за правильным вектором камеры по всему земному шару .
Люк

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

Как мне получить углы камеры здесь?
rafvasq

0

Этот очень простой алгоритм для достижения этого довольно крутой:

Будучи P центральной точкой, которую вы хотите повернуть (точка «взгляда» или «цели»):

translate(-P)

rotate horizontal
rotate vertical

translate(P)

Я использовал это, и это приятно.

Источник найден на дочернем сайте stackoverflow: /programming/287655/opengl-rotating-a-camera-around-a-point

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.