Простая трехмерная проекция и ориентация? - PullRequest
2 голосов
/ 28 сентября 2011

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

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

Камеры позиционируют ориентацию камеры (см. Ниже), поле зрения по высоте и ширине экрана (и соотношение сторон)

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

Есть ли хорошие источники по реализации кватернионов в коде?Должен ли я написать отдельную библиотеку для комплексных чисел?

Спасибо за ваше время и любую помощь:)

1 Ответ

7 голосов
/ 02 января 2013

ВНИМАНИЕ: ДОЛГО ОТВЕТ!

Я сделал аналогичный проект в Love2D, и он работает очень быстро, поэтому я не вижу проблемы с выполнением математики самостоятельно в Lua, а не с использованием OpenGL (который в любом случае не раскрывается).

Вопреки комментариям, не стоит расстраиваться. На самом деле математика 3D-ориентации и перспективы довольно проста, как только вы это почувствуете.

Для ориентации кватернионы, вероятно, излишни. Я обнаружил, что для 3D-проекции с вращением нужны только классы Vec2, Vec3 и Camera. Хотя математически есть несколько тонких различий, на практике Векторы Векторов создают совершенно подходящие матрицы преобразования, а матрицы преобразования - совершенно подходящие ориентации. Матрица, являющаяся вектором векторов, имеет то преимущество, что вам нужно написать только один класс для обработки обоих.

Чтобы спроектировать вектор v, учтите 3 параметра камеры:

  • loc, Vec3 для положения камеры
  • trans, Mat3by3 (также известный как Vec3 из Vec3) для обратного ориентации камеры
    • (отказ от ответственности: использование матриц для ориентации камеры технически считается вредным, поскольку могут накапливаться небольшие ошибки округления, но при фактическом использовании это нормально)
  • zoom, масштабный коэффициент, используемый для определения перспективы. z (относительно камеры) zoom эквивалентно тому, чтобы быть в 2D; то есть без масштабирования с точки зрения.

Проекция работает так:

function Camera:project(v)
    local relv -- v positioned relative to the camera, both in orientation and location
    relv = self.trans * (v - self.loc) -- here '*' is vector dot product
    if relv.z > 0 then
        -- v is in front of the camera
        local w -- perspective scaling factor
        w = self.zoom / relv.z
        local projv -- projected vector
        projv = Vec2(relv.x * w, relv.y * w)
        return projv
    else
        -- v is behind the camera
        return nil
    end
end

это предполагает, что Vec2 (0, 0) соответствует центру окна, а не углу. Это просто перевод.

trans должен начинаться как единичная матрица: Vec3(Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)) и рассчитываться постепенно, внося небольшие корректировки при каждом изменении ориентации.

У меня такое ощущение, что вы уже знаете основы матриц, но если нет, то идея такова: Матрица - это Вектор Векторов, который можно рассматривать, по крайней мере, в этом случае, как систему координат. Каждый вектор можно рассматривать как одну ось системы координат. Изменяя элементы матрицы (которые являются векторами и считаются столбцами матрицы), вы меняете значение координат в этой системе координат. При обычном использовании первый компонент вектора означает движение вправо, второй компонент означает движение вверх, а третий компонент означает движение вперед. Однако с помощью матрицы вы можете направить каждый компонент в произвольном направлении. Определение точечного произведения:

function Vec3.dot(a, b) return a.x * b.x + a.y + b.y + a.z * b.z end

для матрицы

Vec3(axis1, axis2, axis3)

с учетом определения точечного произведения, эта матрица, усеянная вектором v, даст

axis1 * v.x + axis2 * v.y + axis3 * v.z

, что означает, что первый элемент v указывает, сколько axis1 s нужно пройти, второй элемент говорит, сколько axis2 нужно пройти, а третий элемент говорит, сколько axis3 ' s для перемещения, с конечным результатом v, если он выражается в стандартных координатах вместо координат матрицы. Когда мы умножаем матрицу на вектор, мы меняем значение компонентов вектора. По сути, это математическое выражение утверждения типа «все, что было справа, теперь меньше вправо и больше вперед» или что-то подобное. В одном предложении матрица преобразует пробел.

Возвращаясь к текущей задаче, чтобы изобразить поворот в «шагах» (т.е. вокруг оси x) на угол theta, используя матрицу, вы могли бы написать:

function pitchrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        -- we're rotating *around* the x axis, so it stays the same
        Vec3(
            1,
            0,
            0
        ),
        -- axis 2
        -- rotated y axis
        Vec3(
            0,
            math.cos(theta),
            math.sin(theta)
        ),
        -- axis 3
        -- rotated z axis
        Vec3(
            0,
            -math.sin(theta),
            math.cos(theta)
        )
    )
end

и для "рыскания" (вокруг оси y):

function yawrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        Vec3(
            math.cos(theta),
            0,
            math.sin(theta)
        ),
        -- axis 2
        -- rotated y axis
        -- we're rotating *around* the y axis, so it stays the same
        Vec3(
            0,
            1,
            0
        ),
        -- axis 3
        -- rotated z axis
        Vec3(
            -math.sin(theta),
            0,
            math.cos(theta)
        )
    )
end

и, наконец, "крен" (вокруг оси z), что особенно полезно в симуляторе полета:

function rollrotation(theta)
    return Vec3(
        -- axis 1
        -- rotated x axis
        Vec3(
            math.cos(theta),
            math.sin(theta),
            0
        ),
        -- axis 2
        -- rotated y axis
        Vec3(
            -math.sin(theta),
            math.cos(theta),
            0
        ),
        -- axis 3
        -- rotated z axis
        -- we're rotating *around* the z axis, so it stays the same
        Vec3(
            0,
            0,
            1
        )
    )
end

Если вы визуализируете, что это делает с осями x, y и z в вашей голове, все эти косинусы, синусы и смещения знака могут начать иметь смысл. Они все там по причине.

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

function combinematrices(a, b)
    return Vec3(b * a.x, b * a.y, b * a.z) -- x y and z are the first second and third axes
end

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

function Camera:rotateyaw(theta)
    self.trans = combinematrices(self.trans, yawrotation(-theta))
end

мы используем отрицательную тета, потому что мы хотим, чтобы trans была противоположной ориентации камеры, дляпроекция.Вы можете создавать аналогичные функции с помощью Pitch and Roll.

Имея все эти строительные блоки на месте, вы должны быть полностью готовы к написанию 3D-графического кода на Lua.Вы захотите поэкспериментировать с zoom - я обычно использую 500, но это действительно зависит от приложения.

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

Удачного кодирования!Надеюсь, что это было полезно!

...