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
- Esquelet (Skeleton): Conjunt d'ossos que defineixen l'estructura interna del model.
- Ossos (Bones): Elements individuals de l'esquelet que poden ser animats.
- Pells (Skinning): Procés d'assignar vèrtexs de la malla del model als ossos de l'esquelet.
- Pesos (Weights): Valors que determinen la influència de cada os sobre els vèrtexs de la malla.
- Transformacions (Transformations): Matrius que defineixen la posició, rotació i escala dels ossos.
Passos per Implementar l'Animació Esquelètica
- 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; };
- 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]; };
- 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); } }
- 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
- Definir l'Esquelet: Crear un esquelet amb un os per al braç.
- Assignar Pells: Vincular els vèrtexs del braç a l'os corresponent.
- Crear Animació: Definir una animació que mogui el braç amunt i avall.
- 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.
Curs de Programació DirectX
Mòdul 1: Introducció a DirectX
- Què és DirectX?
- Configuració de l'Entorn de Desenvolupament
- Comprendre l'API de DirectX
- Crear la Teva Primera Aplicació DirectX
Mòdul 2: Conceptes Bàsics de Direct3D
- Introducció a Direct3D
- Inicialitzar Direct3D
- Renderitzar un Triangle
- Gestionar el Bucle de Renderització
Mòdul 3: Treballar amb Shaders
Mòdul 4: Tècniques Avançades de Renderització
Mòdul 5: Models 3D i Animació
Mòdul 6: Optimització del Rendiment
- Perfilat i Depuració
- Optimitzar el Rendiment de la Renderització
- Gestió de Memòria
- Multifil en DirectX