КОЛЛАДА: Обратная позиция связывания в неправильном пространстве?

Я работаю над написанием своего собственного импортера COLLADA. Я продвинулся довольно далеко, загружая сетки, материалы и тому подобное. Но я столкнулся с проблемой анимации, а именно: совместных вращений.

Формула, которую я использую для снятия шкур с моей сетки, проста:

weighted;
for (i = 0; i < joint_influences; i++)
{
    weighted += 
        joint[joint_index[i]]->parent->local_matrix * 
        joint[joint_index[i]]->local_matrix * 
        skin->inverse_bind_pose[joint_index[i]] * 
        position * 
        skin->weight[j];
}
position = weighted;

А что касается литературы, это правильная формула. Теперь COLLADA определяет два типа поворотов для суставов: локальный и глобальный. Вы должны объединить вращения вместе, чтобы получить локальное преобразование для соединения.

То, что документация COLLADA не различает, является локальной ротацией соединения и глобальной ротацией соединения. Но в большинстве моделей, которые я видел, у вращений может быть rotate (глобальный) или jointOrient (местный).

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

Это без использования глобальных вращений:

Поза связывания

И это с глобальными вращениями:

странный

На обоих скриншотах я рисую скелет, используя линии, но на первом он невидим, потому что соединения находятся внутри сетки. На втором скриншоте вершины повсюду!

Для сравнения, вот так должен выглядеть второй скриншот:

Collada зритель

Это трудно увидеть, но вы можете видеть, что суставы находятся в правильном положении на втором скриншоте.

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

На этом скриншоте я рисую линию от каждой вершины до соединений, которые имеют влияние. То, что я получаю позу связывания, не так странно, потому что формула теперь становится:

world_matrix * inverse_world_matrix * position * weight

Но это заставляет меня подозревать, что обратная поза связывания COLLADA находится не в том месте.

Поэтому мой вопрос: в каком пространстве COLLADA определяет свою обратную позицию связывания? И как я могу преобразовать обратную позу связывания в нужное мне пространство?

2 ответа

Решение

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

В конце концов я оказался в SceneAnimator::GetBoneMatrices, который содержит следующее:

// Bone matrices transform from mesh coordinates in bind pose to mesh coordinates in skinned pose
// Therefore the formula is offsetMatrix * currentGlobalTransform * inverseCurrentMeshTransform
for( size_t a = 0; a < mesh->mNumBones; ++a)
{
    const aiBone* bone = mesh->mBones[a];
    const aiMatrix4x4& currentGlobalTransform
        = GetGlobalTransform( mBoneNodesByName[ bone->mName.data ]);
    mTransforms[a] = globalInverseMeshTransform * currentGlobalTransform * bone->mOffsetMatrix;
}

globalInverseMeshTransform всегда идентичность, потому что меш ничего не трансформирует. currentGlobalTransform является матрицей связывания, локальными матрицами родительского соединения, соединенными с локальной матрицей соединения. А также mOffsetMatrix является обратной матрицей связывания, которая поступает непосредственно из кожи.

Я проверил значения этих матриц самостоятельно (да, я сравнил их в окне наблюдения), и они были точно такими же, возможно, на 0,0001%, но это несущественно. Так почему же версия Assimp работает, а моя - нет, хотя формула одна и та же?

Вот что я получил:

Инвертирование

Когда Assimp, наконец, загружает матрицы в шейдер скининга, они делают следующее:

helper->piEffect->SetMatrixTransposeArray( "gBoneMatrix", (D3DXMATRIX*)matrices, 60);

Ваааааит секунду. Они загружают их транспонированными? Это не может быть так просто. Ни за что.

Ага.

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

БУДУЩИЕ ГАГЛЕРЫ

  • Прочитайте все преобразования узлов (вращение, перевод, масштабирование и т. Д.) В порядке их получения.
  • Объединить их в локальную матрицу соединения.
  • Возьмите родителя сустава и умножьте его на локальную матрицу.
  • Сохраните это как матрицу связывания.
  • Прочитайте информацию о скине.
  • Сохраните обратную позу матрицы соединения.
  • Сохраните веса суставов для каждой вершины.
  • Умножьте матрицу связывания с обратной матрицей позы связывания и транспонируйте ее, назовите ее матрицей скиннинга.
  • Умножьте матрицу скинов на позицию, умноженную на вес сустава, и добавьте ее к взвешенной позиции.
  • Используйте взвешенную позицию для рендеринга.

Готово!

Кстати, если вы транспонируете матрицы при их загрузке, а не транспонируете матрицу в конце (что может быть проблематично при анимации), вы хотите выполнить умножение по-другому (метод, который вы используете выше, кажется, используется для использования скинов в DirectX при использовании OpenGL дружественные матрицы - значит транспонировать.)

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

XMMATRIX l_oWorldMatrix = XMMatrixMultiply (l_oBindPose, in_oParentWorldMatrix);

XMMATRIX l_oMatrixPallette = XMMatrixMultiply (l_oInverseBindPose, l_oWorldMatrix);

XMMATRIX l_oFinalMatrix = XMMatrixMultiply (l_oBindShapeMatrix, l_oMatrixPallette);

Другие вопросы по тегам