Почему моя камера FPS вращается раз и навсегда? - PullRequest
2 голосов
/ 11 марта 2019

Если я игнорирую грязные детали алгебры кватернионов, я думаю, что я понимаю математику, стоящую за вращением и трансляционными преобразованиями.Но все равно не понимаю, что я делаю неправильно.

Почему моя камера вращается раз и навсегда!? :)

И, если быть более конкретным,как мне вычислить матрицу вида камеры по ее ориентации (матрица вращения)?

Я пишу минималистичный трехмерный движок на Python с классом Node сцены, который обрабатывает механику вращения и перемещения трехмерных объектов.У него есть методы, которые предоставляют матрицы вращения и трансляции, а также матрицу модели.

Существует также класс CameraNode, подкласс Node, который также предоставляет матрицы просмотра и проекции (проекция не является проблемой,поэтому мы можем игнорировать это).

Матрица модели

Чтобы правильно применить преобразования, я умножаю матрицы следующим образом:

PxVxM x v

т.е. сначала Модель, затемВид и, наконец, Проекция.

Где М вычисляется сначала путем применения поворота, а затем перевода:

M = TxR

Вот код:

class Node():
    # ...

    def model_mat(self):
        return self.translation_mat() @ self.rotation_mat() @ self.scaling_mat()

    def translation_mat(self):
        translation = np.eye(4, dtype=np.float32)
        translation[:-1, -1] = self.position  # self.position is an ndarray
        return translation

    def rotation_mat(self):
        rotation = np.eye(4, dtype=np.float32)
        rotation[:-1, :-1] = qua.as_rotation_matrix(self.orientation)  # self.orientation is a quaternion object
        return rotation

ПросмотрМатрица

Я вычисляю матрицу просмотра на основе положения и ориентации камеры следующим образом:

class CameraNode(Node):
    # ...

    def view_mat(self):
        trans = self.translation_mat()
        rot = self.rotation_mat()
        trans[:-1, 3] = -trans[:-1, 3]  # <-- efficient matrix inversion
        rot = rot.T                     # <-- efficient matrix inversion
        self.view = rot @ trans
        return self.view

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

Ввод во вращение

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

def rotate_in_xx(self, pitch):
    rot = qua.from_rotation_vector((pitch, 0.0, 0.0))
    self.orientation *= rot

def rotate_in_yy(self, yaw):
    rot = qua.from_rotation_vector((0.0, yaw, 0.0))
    self.orientation *= rot

ведет себя неправильно, но матрица вращения верна

И этовот что я получаю:

behaves wrong but rot matrix is correct

behaves wrong but rot matrix is correct

Теперь, если я изменю вышеприведенноеСпособы:

class CameraNode(Node):

    def view_mat(self):
        view = np.eye(4)
        trans = self.translation_mat()
        rot = self.rotation_mat()
        trans[:-1, 3] = -trans[:-1, 3]
        # rot = rot.T                     # <-- COMMENTED OUT
        self.view = rot @ trans
        return self.view

    def rotate_in_xx(self, pitch):
        rot = qua.from_rotation_vector((pitch, 0.0, 0.0))
        self.orientation = rot * self.orientation  # <-- CHANGE

Я могу заставить камеру вести себя корректно, как FPS-камера, но матрица вращения кажется неправильной.

behaves well but rot matrix is wrong

enter image description here

Пожалуйста, кто-нибудь может пролить свет?Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 12 марта 2019

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

def rotate_in_xx(self, pitch):
    self.pitch += pitch

def rotate_in_yy(self, yaw):
    self.yaw += yaw

def get_orientation():
    pitch_rotation = qua.from_rotation_vector((self.pitch, 0.0, 0.0))
    yaw_rotation = qua.from_rotation_vector((0.0, self.yaw, 0.0))
    return yaw_rotation * pitch_rotation

Примечание о том, как на вашем последнем снимке экрана матрица вращения камеры и вращение объектаМатрица не идентична: матрицы вращения и перемещения объекта (вместе матрица модели) описывают преобразование из координат объекта в мировых координат , в то время как матрица представления описывает преобразование из мировые координаты до координаты камеры .

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

0 голосов
/ 12 марта 2019

Вы не должны накапливать все свои углы Эйлера за один оборот. Матрица, directionVector или Quaternion.

Делать что-то вроде:

vec3 euler = vec3(yaw, pitch, roll);
rot *= quaternion(euler)
or
self.orientation = quaternion(euler) * self.orientation

Каждый кадр, в который вы добавляете поворот к уже существующему повороту, сохраняетсяв вашей структуре.

float deltaYaw = getInput();
float deltaPitch = getInput();
m_rotation = m_rotation * euler(deltaYaw, deltaRoll, 0.0f) 
is not equal to
m_rotation = rotationMatrix(m_yaw + deltaYaw, m_pitch + deltaRoll, 0.0f);

В одном случае вы поворачиваете уже повернутый объект с его новым 3D-кадром с помощью deltaYaw.Теперь вы применяете рыскание, чтобы учесть бросок, который вы сделали ранее.

oldYaw * oldRoll * deltaYaw != (oldYaw * deltaYaw) * oldRoll

С другой стороны, вы строите поворот от положения вашей сетки до требуемых углов Эйлера.

Да,Вы правы, это не удобный способ работы с камерой, поскольку сохранение переменных рыскания, тангажа и крена в дальнейшем приведет к проблемам (блокировка gimBall, анимация камеры будет сложной ...).Я бы порекомендовал посмотреть на камеру arcBall https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball

...