КОЛЛАДА: Обратная связь позы в неправильном пространстве?
Я работаю над написанием собственного импортера 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);