Вращение, перемещение и расположение камеры по умолчанию

Я просто играю с настройкой шаблона в MTKView; и я пытался понять следующее:

  1. Расположение камеры по умолчанию.

  2. Расположение по умолчанию при создании примитивов с использованием MDLMesh и MTKMesh.

  3. Почему вращение включает в себя и перевод.

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

matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, 5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));
matrix_float4x4 base_mv = matrix_multiply(_viewMatrix, base_model);
matrix_float4x4 modelViewMatrix = matrix_multiply(base_mv, matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));

Предыдущий код взят из метода _update по шаблону; очевидно, он пытается вращать модель вместо камеры. Но что меня смущает, так это то, что это требует еще и перевода. Я читал такие утверждения, как «потому что это всегда вращается на (0, 0, 0)». Но зачем (0, 0, 0), если объект находится в другом месте? Кроме того, мне кажется, что камера смотрит на положительную ось Z (вопрос 1) вместо обычной отрицательной оси Z, потому что если я изменю:

matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, 5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));

to:

matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, -5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));

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

Если я установлю matrix_from_translation(0.0f, 0.0f, 0.0f) (все нули), объект просто повернется не по оси Y (вопрос 3), как я ожидал.

Я попытался выяснить, где по умолчанию размещаются MDLMesh и MTKMesh (вопрос 2), но не смог найти свойство, которое регистрирует их положение. Ниже показано, также по шаблону, как создается примитив:

MDLMesh *mdl = [MDLMesh newBoxWithDimensions:(vector_float3){2,2,2} segments:(vector_uint3){1,1,1}
                geometryType:MDLGeometryTypeTriangles inwardNormals:NO
                allocator:[[MTKMeshBufferAllocator alloc] initWithDevice: _device]];

_boxMesh = [[MTKMesh alloc] initWithMesh:mdl device:_device error:nil];

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

Спасибо.


person Unheilig    schedule 24.10.2016    source источник
comment
Есть несколько действительно замечательных книг по трехмерной математике. Я рекомендую «3D Math Primer for Graphics and Game Development» Данна и «Essential Mathematics for Games and Interactive Applications». Вещи, с которыми у вас возникают проблемы, называются преобразованием представления модели и просмотром. Это должно помочь вам с поиском в Google. А также умножение матриц. @warrenm дал отличный ответ.   -  person Bjorn    schedule 25.10.2016


Ответы (1)


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

Я заменил эту последнюю матрицу на матрицу из шаблона, поскольку ваша модификация просто удваивает вращение вокруг оси Y.

modelViewMatrix = identity *
                  translate(0, 0, 5) *
                  rotate(angle, axis(0, 1, 0)) *
                  rotate(angle, axis(1, 1, 1))

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

Сначала вращаем куб вокруг оси (1, 1, 1), которая проходит по диагонали через начало координат. Затем мы вращаем куб вокруг оси Y. Эти вращения объединяются, чтобы сформировать своего рода анимацию «кувырка». Затем мы смещаем куб на 5 единиц по оси +Z (которая, как вы заметили, уходит на экран, поскольку мы рассматриваем наш мир как левосторонний). Наконец, мы применяем преобразование камеры, которое жестко закодировано как матрица идентичности. Мы могли бы использовать дополнительное положительное смещение по +Z в качестве матрицы камеры, чтобы отодвинуть куб еще дальше от камеры, или отрицательное значение, чтобы переместить куб ближе.

Чтобы ответить на ваши вопросы:

  1. Для камеры нет местоположения по умолчанию, кроме источника (0, 0, 0), если вы хотите так думать. Вы «позиционируете» камеру в мире, умножая положение вершин на обратное преобразование, которое представляет, как камера размещена в мире.

  2. Ввод-вывод модели строит сетки, которые «центрируются» вокруг начала координат в той мере, в какой это имеет смысл для генерируемой формы. Цилиндры, эллипсоиды и параллелепипеды на самом деле центрируются вокруг начала координат, в то время как конусы построены так, что их вершина находится в начале координат, а их ось проходит вдоль -Y.

  3. Вращение на самом деле не столько связано с переводом, сколько в сочетании с ним. Причина перевода в том, что нам нужно расположить куб подальше от камеры; иначе мы были бы внутри него при рисовании.

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

person warrenm    schedule 25.10.2016
comment
Привет, Уоррен; Спасибо за ответ. Что касается порядка умножения: я думал, что порядок имеет значение только тогда, когда умножения происходят внутри шейдера (?); таким образом, я читал его слева направо. Я полагал, что, поскольку мы вызываем matrix_multiply несколько раз, как система может знать порядок между этими вызовами? Если бы это был только один matrix_multiply, то я бы понял, что он выполняет умножение в правильном порядке. (продолжение) - person Unheilig; 25.10.2016
comment
Что касается положения камеры: можем ли мы сказать, что (0, 0, 0) находится прямо посередине экрана, где находится камера и где центрируются сетки ввода/вывода? Наконец, меня смутило то, что в GL при вращении объекта вокруг его оси обычно нужно было делать: glTranslate(.., .., some negative number), glRotate(.., .., ..) и затем glTranslate(.., .., a positive number of the negative number from the preceding glTranslate); Вы случайно не знаете, почему нам нужно было сделать это в GL, а не в Metal, как в нашем примере здесь? Спасибо. - person Unheilig; 25.10.2016
comment
@Unheilig Порядок умножения важен, потому что умножение матриц не является коммутативным. А(В) != В(А). Вы можете легко убедиться в этом самостоятельно, умножив тривиальную матрицу 2x2 в обоих направлениях. Умножение матриц является ассоциативным, поэтому вы можете назвать C = A (B) и F = D (E), а C (F) было бы равно, если бы вы сделали A (B (D (E))). Очень часто предварительно умножают преобразование вида матричной модели в коде приложения, а затем завершают умножение каждой вершины на результат в шейдере. Вам нужно правильно умножить его в шейдере в том порядке, в котором оно было предварительно умножено. - person Bjorn; 25.10.2016
comment
@Unheilig То, что вы описываете, - это как вращаться вокруг произвольной оси в произвольной системе отсчета. Причина, по которой вам не нужно использовать здесь технику T^-1(R)T, заключается в том, что все повороты происходят относительно начала мировой системы координат, поэтому и T, и T^-1 идентичны. - person warrenm; 25.10.2016
comment
Правильно :-) Теперь я вижу разницу. Большое спасибо, особенно за ваше терпение. - person Unheilig; 25.10.2016