Понимание части оформления файла GLTF2.0 для движка OpenGL - PullRequest
3 голосов
/ 05 мая 2019

У меня есть простая модель блендера, которая состоит из трех сеток с тремя костями, контролирующими одну сетку каждая. Анимация - это просто кости, немного вращающие кубы вокруг оси y и обратно. Центральная кость является родителем двух внешних костей.

Blender screenshot

Затем я экспортирую эту сцену с помощью плагина экспорта GLTF2.0 (текстовая версия) и сейчас пытаюсь импортировать ее в мой недавно созданный движок opengl (c # xamarin android).

Поскольку я хочу полностью понять формат GLTF2.0 и скелетную анимацию в OpenGL, я пытаюсь реализовать чтение GLTF2.0 самостоятельно.

Я прочитал:

Отображать сетки было легко, но теперь я застрял, заставляя анимацию работать. В моем файле gltf я вижу три скина:

"skins" : [
    {
        "inverseBindMatrices" : 21,
        "joints" : [
            4,
            5,
            6
        ],
        "skeleton" : 0
    },
    {
        "inverseBindMatrices" : 22,
        "joints" : [
            4,
            5,
            6
        ],
        "skeleton" : 0
    },
    {
        "inverseBindMatrices" : 23,
        "joints" : [
            4,
            5,
            6
        ],
        "skeleton" : 0
    }
]

Что меня смущает, потому что у меня одна структура кости для всех сеток, а не три кости для каждой сетки. Я думал, что соберу все кости в экземплярах классов (скажем, Bone.cs), у каждой кости будет список дочерних костей и поле для родительской кости. Затем я собирал анимации в экземплярах (класс Animation.cs), и у каждого экземпляра анимации был бы список ключевых кадров, содержащих вращение, масштабирование и перевод для данной временной метки. Когда для временной метки анимации установлено значение 2,5 секунды, я просматриваю ближайшие два ключевых кадра для этой временной метки и интерполирую вращение, масштабирование и перевод для этих ключевых кадров.

Актуальные вопросы

  • Почему там три скина? Почему inverseBindMatrices связан с кожей, а не с суставом?
  • Когда у меня есть правильное вращение, масштабирование, перевод из ключевого кадра (на кость), как рассчитать матрицы для каждой кости, которые мне нужно передать в мой вершинный шейдер?
  • Каждый костный узел в файле имеет свои собственные значения поворота, перемещения и масштаба, но не имеет матрицы. Это почему? Чего-то не хватает?
  • Файл gltf ссылается на кость (сустав) как идентификатор узла, но массивы weights / jointId-Array, которые передаются в качестве атрибутов шейдеру, не соответствуют этим идентификаторам костей: jointIds-Array содержит, например, 0,1,2 для идентификаторов костей, но кости находятся в узлах 4,5,6 - как найти правильную кость для каждого сустава, переданного в шейдер?

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

Редактировать

Пример загрузки модели GLTF

Редактировать # 2

Хорошо, я думаю, что понимаю это ... медленно.

  • Для каждой сетки, контролируемой арматурой, в файле есть один скин. Я думаю, что для каждой сетки должна быть обратная матрица связывания, чтобы можно было преобразовать сетку в костное пространство (и, если нужно, обратно).

  • Я до сих пор не знаю, как правильно рассчитать окончательные преобразования перед передачей их в шейдер.

  • Этот момент все еще ускользает от меня.

  • Поскольку каждый скин имеет список из трех (или не более 4) соединений, это соединения, из которых необходимо передать окончательные преобразования в вершинный шейдер. Если у вас есть 8 суставов, но на текущую сетку, которая будет нарисована, влияют только 4 из них, почему вы должны пропустить все 8 матриц, а не только 4, которые вам нужны.

Это все еще окутано сомнением. Может быть, это поможет кому-то еще.

1 Ответ

3 голосов
/ 07 мая 2019

Я пытаюсь ответить на ваши вопросы один за другим

  • Почему там три скина? Почему inverseBindMatrices связан с кожей, а не с суставом?

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

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

Существует обратная матрица связывания для каждого соединения из каждой сетки . Имя свойства - inverseBindMatrices во множественном числе по причине, и оно ссылается на bufferview, которое в свою очередь ссылается на некоторые данные в buffer.

Здесь меняется порядок вашего вопроса, потому что таким образом это будет иметь больше смысла:

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

Что еще тебе нужно? Каждое аффинное преобразование может быть разложено на перевод, вращение и масштабирование, поэтому данные полны. Спецификация glTF определяет, что полученная матрица должна быть вычислена в следующем порядке: T*R*S.

  • Когда у меня есть правильное вращение, масштабирование, перевод из ключевого кадра (на кость), как рассчитать матрицы для каждой кости, которые мне нужно передать в мой вершинный шейдер?

Для каждого костного узла i вы можете рассчитать локальное преобразование как M_local(i) = T(i)*R(i)*S(i). Вы получите объединенные матрицы, применяя полную иерархию, поэтому в основном M_global(i) = M_global(parent(i)) * M_local(i), а затем можете построить матрицы объединения как M_joint(i) = inverse(globalTransform) * M_global(i) * inverseBindMatrix(i).

  • Файл gltf ссылается на кость (сустав) как идентификатор узла, но массивы weights / jointId-Array, которые передаются в качестве атрибутов шейдеру, не соответствуют этим идентификаторам костей: jointIds-Array содержит, например, 0,1,2 для идентификаторов костей, но кости находятся в узлах 4,5,6 - как найти правильную кость для каждого соединения, переданного в шейдер?

Массив jointIds содержит ссылки на суставы, а не кости (отсюда и название). Скининговый шейдер вообще не заботится о костях, все, что делают кости, - это определяет иерархию суставов, поэтому они влияют на действительные значения M_global и, следовательно, также M_joint матриц. i -я запись просто ссылается на i -й сустав в массиве joints соответствующего скина, поэтому ей нужно M_joint(i).

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

Почему бы skin быть ограничено 4 суставами. Кожа может иметь столько суставов, сколько ей нравится.

Если у вас есть 8 суставов, но на текущую сетку, на которую нужно нарисовать, влияют только 4 из них, почему вы должны пропустить все 8 матриц, а не только 4, которые вам нужны.

Зачем вам определять шкуру из 8 костей для меша, которому нужно только четыре? Формат данных glTF не мешает вам хранить нерелевантную информацию или хранить информацию неэффективным способом.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...