Матричные расчеты для скиннинга GPU

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

Что именно мне нужно с костями? offsetMatrix переменная? На что мне нужно умножить это?

3 ответа

Решение

Давайте возьмем, к примеру, этот код, который я использовал для анимации персонажей в игре, над которой я работал. Я также использовал 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 или правильно преобразование корневого узла scene->mRootNode->mTransformation это преобразование из локального пространства в глобальное пространство. Например, когда в 3D-моделере (например, давайте выберем Blender) вы создаете свой меш или загружаете своего персонажа, он обычно располагается (по умолчанию) в начале декартовой плоскости, а его вращение устанавливается на личность кватерниона.

Однако вы можете перевести / повернуть вашу сетку / персонажа от начала координат (0,0,0) куда-то еще и иметь в одной сцене даже несколько сеток с разных позиций. Когда вы загружаете их, особенно если вы делаете скелетную анимацию, их обязательно нужно переводить обратно в локальное пространство (то есть обратно в начало координат). 0,0,0) и это причина, почему вы должны умножить все на InverseGlobal (который возвращает вашу сетку в локальное пространство).

После этого вам нужно умножить его на преобразование узла, которое является умножением parentTransform (преобразование на один уровень выше в дереве, это общее преобразование) transform (ранее assimp_node->mTransformation это просто преобразование кости относительно родителя узла) и ваше локальное преобразование (любое 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 мы бы взяли offset Matrix и применили его к вершинам, на которые воздействует кость, когда мы строим буфер вершин. И затем, когда мы анимируем меш, мы позже применяем анимированную матрицу для этой кости.

В случае #2 мы будем использовать offset Matrix в сочетании с матрицей анимации для этой кости при преобразовании вершин, хранящихся в буфере вершин. Так что это будет что-то вроде (Примечание: вам, возможно, придется переключать конкатенации матрицы здесь);

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

Другие вопросы по тегам