L'animació esquelètica és una tècnica d'animació utilitzada per animar models 3D de manera eficient. Aquesta tècnica implica l'ús d'un esquelet intern (conjunt d'ossos) que controla la deformació de la malla del model. Cada os de l'esquelet pot ser animat individualment, permetent moviments complexos i realistes.

Conceptes Clau

  1. Esquelet (Skeleton): Conjunt d'ossos que defineixen l'estructura interna del model.
  2. Ossos (Bones): Elements individuals de l'esquelet que poden ser animats.
  3. Pells (Skinning): Procés d'assignar vèrtexs de la malla del model als ossos de l'esquelet.
  4. Pesos (Weights): Valors que determinen la influència de cada os sobre els vèrtexs de la malla.
  5. Transformacions (Transformations): Matrius que defineixen la posició, rotació i escala dels ossos.

Passos per Implementar l'Animació Esquelètica

  1. Definir l'Esquelet

L'esquelet es defineix com una jerarquia d'ossos. Cada os té una posició relativa al seu os pare.

struct Bone {
    std::string name;
    int parentIndex;
    DirectX::XMFLOAT4X4 bindPose;
    DirectX::XMFLOAT4X4 inverseBindPose;
    DirectX::XMFLOAT4X4 currentTransform;
};

  1. Assignar Pells

Cada vèrtex de la malla es vincula a un o més ossos amb un pes associat.

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

  1. Aplicar Transformacions

Les transformacions dels ossos es calculen i s'apliquen als vèrtexs de la malla.

void ApplyBoneTransformations(std::vector<Bone>& bones, std::vector<Vertex>& vertices) {
    for (auto& bone : bones) {
        if (bone.parentIndex >= 0) {
            bone.currentTransform = bone.bindPose * bones[bone.parentIndex].currentTransform;
        } else {
            bone.currentTransform = bone.bindPose;
        }
    }

    for (auto& vertex : vertices) {
        DirectX::XMVECTOR transformedPosition = DirectX::XMVectorZero();
        for (int i = 0; i < 4; ++i) {
            if (vertex.boneWeights[i] > 0) {
                DirectX::XMMATRIX boneTransform = DirectX::XMLoadFloat4x4(&bones[vertex.boneIndices[i]].currentTransform);
                transformedPosition += vertex.boneWeights[i] * DirectX::XMVector3Transform(DirectX::XMLoadFloat3(&vertex.position), boneTransform);
            }
        }
        DirectX::XMStoreFloat3(&vertex.position, transformedPosition);
    }
}

  1. Animar l'Esquelet

Les animacions es defineixen com una sèrie de transformacions aplicades als ossos al llarg del temps.

struct Keyframe {
    float time;
    DirectX::XMFLOAT4X4 transform;
};

struct Animation {
    std::string name;
    float duration;
    std::vector<std::vector<Keyframe>> boneKeyframes;
};

void UpdateAnimation(Animation& animation, float time, std::vector<Bone>& bones) {
    for (size_t i = 0; i < bones.size(); ++i) {
        const auto& keyframes = animation.boneKeyframes[i];
        if (keyframes.empty()) continue;

        // Interpolació lineal entre keyframes
        size_t k1 = 0, k2 = 1;
        while (k2 < keyframes.size() && keyframes[k2].time < time) {
            k1 = k2++;
        }

        float t = (time - keyframes[k1].time) / (keyframes[k2].time - keyframes[k1].time);
        DirectX::XMMATRIX transform1 = DirectX::XMLoadFloat4x4(&keyframes[k1].transform);
        DirectX::XMMATRIX transform2 = DirectX::XMLoadFloat4x4(&keyframes[k2].transform);
        DirectX::XMMATRIX interpolatedTransform = DirectX::XMMatrixLerp(transform1, transform2, t);

        DirectX::XMStoreFloat4x4(&bones[i].bindPose, interpolatedTransform);
    }
}

Exercici Pràctic

Objectiu

Implementar una animació esquelètica simple que faci que un model 3D mogui el braç amunt i avall.

Passos

  1. Definir l'Esquelet: Crear un esquelet amb un os per al braç.
  2. Assignar Pells: Vincular els vèrtexs del braç a l'os corresponent.
  3. Crear Animació: Definir una animació que mogui el braç amunt i avall.
  4. Aplicar Transformacions: Actualitzar les transformacions dels ossos i aplicar-les als vèrtexs.

Solució

// Definició de l'esquelet
std::vector<Bone> bones = {
    {"Root", -1, IdentityMatrix(), IdentityMatrix(), IdentityMatrix()},
    {"Arm", 0, IdentityMatrix(), IdentityMatrix(), IdentityMatrix()}
};

// Assignació de pells
std::vector<Vertex> vertices = {
    // Vèrtexs del braç
    {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 0.0f}, {1}, {1.0f}},
    {{1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1}, {1.0f}},
    // Altres vèrtexs...
};

// Definició de l'animació
Animation armAnimation = {
    "ArmWave",
    2.0f,
    {
        // Keyframes per a l'os del braç
        {
            {0.0f, IdentityMatrix()},
            {1.0f, RotationMatrix(DirectX::XM_PI / 2, {0.0f, 0.0f, 1.0f})},
            {2.0f, IdentityMatrix()}
        }
    }
};

// Actualització de l'animació i aplicació de transformacions
float currentTime = 0.0f;
UpdateAnimation(armAnimation, currentTime, bones);
ApplyBoneTransformations(bones, vertices);

Conclusió

L'animació esquelètica és una tècnica poderosa per animar models 3D de manera eficient i realista. Comprendre els conceptes clau i els passos per implementar aquesta tècnica és essencial per a qualsevol desenvolupador que treballi amb animacions 3D. Amb la pràctica, es poden crear animacions complexes i realistes per a jocs i aplicacions gràfiques.

En el següent tema, explorarem l'animació de targetes de morfologia, una altra tècnica d'animació utilitzada per deformar models 3D.

© Copyright 2024. Tots els drets reservats