La animación esquelética es una técnica de animación utilizada para animar personajes y objetos complejos en 3D. En lugar de animar cada vértice de un modelo 3D individualmente, la animación esquelética utiliza un conjunto de huesos (esqueleto) para controlar la deformación del modelo. Esta técnica es eficiente y permite crear animaciones realistas y complejas con menos esfuerzo.

Conceptos Clave

  1. Esqueleto (Skeleton): Una estructura jerárquica de huesos que define la posición y orientación de cada parte del modelo.
  2. Huesos (Bones): Elementos individuales del esqueleto que pueden moverse y rotar para deformar el modelo.
  3. Piel (Skin): El modelo 3D que se deforma según el movimiento de los huesos.
  4. Pesos de Influencia (Weights): Valores que determinan cuánto afecta cada hueso a los vértices del modelo.
  5. Matrices de Transformación (Transformation Matrices): Matrices que definen la posición, rotación y escala de los huesos.

Pasos para Implementar Animación Esquelética

  1. Definir el Esqueleto

El primer paso es definir el esqueleto del modelo. Esto incluye crear una jerarquía de huesos y establecer sus posiciones iniciales.

struct Bone {
    std::string name;
    int parentIndex;
    DirectX::XMMATRIX offsetMatrix;
    DirectX::XMMATRIX finalTransformation;
};

std::vector<Bone> skeleton;

  1. Asociar el Esqueleto con el Modelo

Cada vértice del modelo debe estar asociado con uno o más huesos. Esto se hace mediante pesos de influencia.

struct Vertex {
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT2 texCoord;
    int boneIndices[4];
    float weights[4];
};

std::vector<Vertex> vertices;

  1. Calcular las Transformaciones de los Huesos

Para cada cuadro de animación, se deben calcular las transformaciones de los huesos. Esto incluye aplicar las matrices de transformación y combinar las transformaciones de los huesos padres.

void UpdateBoneTransformations(std::vector<Bone>& skeleton, const std::vector<DirectX::XMMATRIX>& animations) {
    for (size_t i = 0; i < skeleton.size(); ++i) {
        if (skeleton[i].parentIndex >= 0) {
            skeleton[i].finalTransformation = animations[i] * skeleton[skeleton[i].parentIndex].finalTransformation;
        } else {
            skeleton[i].finalTransformation = animations[i];
        }
    }
}

  1. Deformar el Modelo

Usando las transformaciones de los huesos, se deforma el modelo para cada cuadro de animación.

void ApplySkinning(std::vector<Vertex>& vertices, const std::vector<Bone>& skeleton) {
    for (auto& vertex : vertices) {
        DirectX::XMVECTOR skinnedPosition = DirectX::XMVectorZero();
        for (int i = 0; i < 4; ++i) {
            if (vertex.weights[i] > 0) {
                DirectX::XMMATRIX boneTransform = skeleton[vertex.boneIndices[i]].finalTransformation;
                skinnedPosition += vertex.weights[i] * DirectX::XMVector3Transform(XMLoadFloat3(&vertex.position), boneTransform);
            }
        }
        DirectX::XMStoreFloat3(&vertex.position, skinnedPosition);
    }
}

Ejercicio Práctico

Ejercicio 1: Implementar una Animación Esquelética Simple

  1. Definir el Esqueleto: Crea un esqueleto simple con al menos 3 huesos.
  2. Asociar el Esqueleto con un Modelo: Crea un modelo simple (por ejemplo, un cubo) y asocia los vértices con los huesos.
  3. Calcular las Transformaciones de los Huesos: Implementa una función para calcular las transformaciones de los huesos.
  4. Deformar el Modelo: Implementa la deformación del modelo usando las transformaciones de los huesos.

Solución

#include <vector>
#include <DirectXMath.h>
#include <string>

struct Bone {
    std::string name;
    int parentIndex;
    DirectX::XMMATRIX offsetMatrix;
    DirectX::XMMATRIX finalTransformation;
};

struct Vertex {
    DirectX::XMFLOAT3 position;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT2 texCoord;
    int boneIndices[4];
    float weights[4];
};

void UpdateBoneTransformations(std::vector<Bone>& skeleton, const std::vector<DirectX::XMMATRIX>& animations) {
    for (size_t i = 0; i < skeleton.size(); ++i) {
        if (skeleton[i].parentIndex >= 0) {
            skeleton[i].finalTransformation = animations[i] * skeleton[skeleton[i].parentIndex].finalTransformation;
        } else {
            skeleton[i].finalTransformation = animations[i];
        }
    }
}

void ApplySkinning(std::vector<Vertex>& vertices, const std::vector<Bone>& skeleton) {
    for (auto& vertex : vertices) {
        DirectX::XMVECTOR skinnedPosition = DirectX::XMVectorZero();
        for (int i = 0; i < 4; ++i) {
            if (vertex.weights[i] > 0) {
                DirectX::XMMATRIX boneTransform = skeleton[vertex.boneIndices[i]].finalTransformation;
                skinnedPosition += vertex.weights[i] * DirectX::XMVector3Transform(XMLoadFloat3(&vertex.position), boneTransform);
            }
        }
        DirectX::XMStoreFloat3(&vertex.position, skinnedPosition);
    }
}

int main() {
    // Definir el esqueleto
    std::vector<Bone> skeleton = {
        {"Root", -1, DirectX::XMMatrixIdentity(), DirectX::XMMatrixIdentity()},
        {"Bone1", 0, DirectX::XMMatrixTranslation(0.0f, 1.0f, 0.0f), DirectX::XMMatrixIdentity()},
        {"Bone2", 1, DirectX::XMMatrixTranslation(0.0f, 1.0f, 0.0f), DirectX::XMMatrixIdentity()}
    };

    // Definir el modelo
    std::vector<Vertex> vertices = {
        {{0.0f, 0.0f, 0.0f}, {}, {}, {0, 1, 2, -1}, {0.5f, 0.3f, 0.2f, 0.0f}},
        // Añadir más vértices según sea necesario
    };

    // Definir las animaciones (matrices de transformación)
    std::vector<DirectX::XMMATRIX> animations = {
        DirectX::XMMatrixRotationY(0.5f),
        DirectX::XMMatrixRotationX(0.5f),
        DirectX::XMMatrixRotationZ(0.5f)
    };

    // Actualizar las transformaciones de los huesos
    UpdateBoneTransformations(skeleton, animations);

    // Aplicar el skinning
    ApplySkinning(vertices, skeleton);

    return 0;
}

Conclusión

La animación esquelética es una técnica poderosa para animar modelos 3D de manera eficiente y realista. Al comprender y aplicar los conceptos de esqueleto, huesos, pesos de influencia y matrices de transformación, puedes crear animaciones complejas y fluidas. En el siguiente módulo, exploraremos técnicas avanzadas de animación y cómo optimizar el rendimiento de tus animaciones esqueléticas.

© Copyright 2024. Todos los derechos reservados