Я пытаюсь сделать скелетную анимацию в OpenGL, используя Assimp в качестве моей библиотеки импорта модели.
Что мне нужно для переменной "offsetMatrix
"? Что мне нужно, чтобы умножить его на?
Я пытаюсь сделать скелетную анимацию в OpenGL, используя Assimp в качестве моей библиотеки импорта модели.
Что мне нужно для переменной "offsetMatrix
"? Что мне нужно, чтобы умножить его на?
Возьмем, к примеру, этот код, который я использовал для анимации персонажей в игре, в которой я работал. Я также использовал Assimp, чтобы загрузить информацию о костях, и я прочитал сам учебник OGL, уже упомянутый Нико.
glm::mat4 getParentTransform()
{
if (this->parent)
return parent->nodeTransform;
else
return glm::mat4(1.0f);
}
void updateSkeleton(Bone* bone = NULL)
{
bone->nodeTransform = bone->getParentTransform() // This retrieve the transformation one level above in the tree
* bone->transform //bone->transform is the assimp matrix assimp_node->mTransformation
* bone->localTransform; //this is your T * R matrix
bone->finalTransform = inverseGlobal // which is scene->mRootNode->mTransformation from assimp
* bone->nodeTransform //defined above
* bone->boneOffset; //which is ai_mesh->mBones[i]->mOffsetMatrix
for (int i = 0; i < bone->children.size(); i++) {
updateSkeleton (&bone->children[i]);
}
}
По существу GlobalTransform
, как указано в учебнике Скелетная анимация с Assimp или правильно преобразование корня node scene->mRootNode->mTransformation
является преобразованием из локального пространства в глобальное пространство. Чтобы привести пример, когда в 3D-моделире (например, выберите Blender), вы создаете свою сетку или загружаете свой символ, она обычно позиционируется (по умолчанию) в начале декартовой плоскости и ее вращение устанавливается на идентичность кватерниона.
Однако вы можете перевести/повернуть ваш меш/символ из источника (0,0,0)
в другое место и иметь в одной сцене даже несколько сеток с разными позициями. Когда вы загружаете их, особенно если вы выполняете скелетную анимацию, необходимо перевести их обратно в локальное пространство (т.е. Обратно в начало 0,0,0
), и именно по этой причине вам нужно умножить все на InverseGlobal
(который возвращает вашу сетку в локальное пространство).
После этого вам нужно умножить его на преобразование node, которое является умножением parentTransform
(преобразование на один уровень вверх в дереве, это общее преобразование) transform
(ранее assimp_node->mTransformation
который представляет собой просто преобразование кости относительно родителя node) и ваше локальное преобразование (любое T * R), которое вы хотите применить для: кинематической, обратной кинематической или ключевой кадровой интерполяции.
В конце концов существует функция boneOffset (ai_mesh->mBones[i]->mOffsetMatrix
), которая преобразуется из пространства сетки в пространство костей в позиции привязки, как указано в документации.
Здесь есть ссылка на GitHub, если вы хотите посмотреть весь код для моего класса Skeleton.
Надеюсь, что это поможет.
Матрица смещения определяет преобразование (перевод, масштаб, поворот), которое преобразует вершину в пространстве сетки и преобразует ее в "кодовое" пространство. В качестве примера рассмотрим следующую вершину и кость со следующими свойствами:
Vertex Position<0, 1, 2>
Bone Position<10, 2, 4>
Bone Rotation<0,0,0,1> // Note - no rotation
Bone Scale<1, 1, 1>
Если мы умножим вершину на матрицу смещения, в этом случае мы получим вершинную позицию < -10, -1, 2 > .
Как мы это используем? У вас есть два варианта использования этой матрицы, которая сводится к тому, как мы храним данные вершин в буферах вершин. Возможные варианты:
1) Хранить сетчатые вершины в пространстве сетки 2) Храните сетчатые вершины в костном пространстве.
В случае # 1 мы возьмем offsetMatrix и применим его к вершинам, на которые влияет кость, когда мы создаем буфер вершин. И затем, когда мы анимируем сетку, мы позже применяем анимированную матрицу для этой кости.
В случае # 2 мы будем использовать offsetMatrix в сочетании с матрицей анимации для этой кости при преобразовании вершин, хранящихся в буфере вершин. Так что это будет что-то вроде (Примечание: вам, возможно, придется переключать конкатенации матрицы здесь);
anim_vertex = (offset_matrix * anim_matrix) * mesh_vertex
Помогает ли это?
Как я уже предполагал, mOffsetMatrix
представляет собой матрицу позывной обратной привязки. В этом руководстве указаны правильные преобразования, которые необходимы для линейной скинов:
Сначала вам нужно оценить состояние анимации. Это даст вам системное преобразование из анимированного пространства костей в мировое пространство для каждой кости (GlobalTransformation
в учебнике). mOffsetMatrix
- это системное преобразование из мирового пространства, чтобы связать позу кости. Таким образом, то, что вы делаете для скининга, следующее (предполагая, что на определенную вершину влияет одна кость): преобразуйте вершину в пространство кости с помощью mOffsetMatrix
. Теперь предположим анимированную кость и преобразуем промежуточный результат обратно из анимированного пространства костей в мировое пространство. Итак:
boneMatrix[i] = animationMatrix[i] * mOffsetMatrix[i]
Если на вершину влияют несколько костей, LBS просто усредняет результаты. То, где весов вступает в игру. Скиннинг обычно реализуется в вершинном шейдере:
vec4 result = vec4(0);
for each influencing bone i
result += weight[i] * boneMatrix[i] * vertexPos;
Обычно максимальное число влияющих костей фиксировано, и вы можете развернуть цикл for
.
В руководстве используется дополнительный m_GlobalInverseTransform
для boneMatrix
. Однако я не знаю, почему они это делают. В основном это нарушает общую трансформацию всей сцены. Вероятно, он используется для центрирования модели в представлении.