КОЛЛАДА: Обратная позиция связывания в неправильном пространстве? - PullRequest
13 голосов
/ 29 сентября 2011

Я работаю над написанием своего собственного импортера 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 (локальный).

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

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

Bind pose

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

Weird

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

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

Collada viewer

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

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

enter image description here

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

world_matrix * inverse_world_matrix * position * weight

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

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

Ответы [ 2 ]

12 голосов
/ 02 октября 2011

Я начал со сравнения моих значений со значениями, которые я прочитал из 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 работает, а моя - нет, хотя формула одна и та же?

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

Inversed!

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

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

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

enter image description here

Да.

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

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

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

Готово!

1 голос
/ 16 мая 2013

Кстати, если вы транспонируете матрицы при их загрузке, а не транспонируете матрицу в конце (что может быть проблематично при анимации), вы хотите выполнить умножение по-другому (метод, который вы используете выше, кажется, используется для использования скинов в 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);

...