Матричные вычисления для скинов gpu

Я пытаюсь сделать скелетную анимацию в OpenGL, используя Assimp в качестве моей библиотеки импорта модели.

Что мне нужно для переменной "offsetMatrix"? Что мне нужно, чтобы умножить его на?

Ответ 1

Возьмем, к примеру, этот код, который я использовал для анимации персонажей в игре, в которой я работал. Я также использовал 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.

Надеюсь, что это поможет.

Ответ 2

Матрица смещения определяет преобразование (перевод, масштаб, поворот), которое преобразует вершину в пространстве сетки и преобразует ее в "кодовое" пространство. В качестве примера рассмотрим следующую вершину и кость со следующими свойствами:

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

Помогает ли это?

Ответ 3

Как я уже предполагал, 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. Однако я не знаю, почему они это делают. В основном это нарушает общую трансформацию всей сцены. Вероятно, он используется для центрирования модели в представлении.