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

Я работаю над написанием собственного импортера 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 указывает свою обратную привязку? И как я могу преобразовать позыв обратной привязки в нужное мне пространство?

Ответ 1

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

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

Inversed!

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

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

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

enter image description here

Да.

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

БУДУЩИЕ GOOGLERS

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

Готово!

Ответ 2

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

В 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);