In this section, we will explore the fundamentals of animating 3D models in DirectX. Animation is a crucial aspect of creating dynamic and engaging 3D applications, such as games and simulations. We will cover the following topics:

  1. Understanding Animation Basics
  2. Keyframe Animation
  3. Interpolating Between Keyframes
  4. Implementing Animation in DirectX
  5. Practical Exercises

Understanding Animation Basics

Animation in 3D graphics involves changing the properties of objects over time to create the illusion of movement. The primary properties that can be animated include:

  • Position: Moving an object from one place to another.
  • Rotation: Rotating an object around an axis.
  • Scale: Changing the size of an object.
  • Morphing: Changing the shape of an object.

Keyframe Animation

Keyframe animation is a technique where specific "key" frames are defined at certain points in time, and the intermediate frames are interpolated between these keyframes. This method is widely used due to its simplicity and efficiency.

Example of Keyframe Data Structure

struct Keyframe {
    float time; // Time of the keyframe
    DirectX::XMFLOAT3 position; // Position at this keyframe
    DirectX::XMFLOAT4 rotation; // Rotation (quaternion) at this keyframe
    DirectX::XMFLOAT3 scale; // Scale at this keyframe
};

Example Keyframe Sequence

std::vector<Keyframe> keyframes = {
    {0.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}},
    {1.0f, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.707f, 0.707f}, {1.0f, 1.0f, 1.0f}},
    {2.0f, {2.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 1.0f}}
};

Interpolating Between Keyframes

To create smooth animations, we need to interpolate between keyframes. Linear interpolation (LERP) is commonly used for position and scale, while spherical linear interpolation (SLERP) is used for rotations.

Linear Interpolation (LERP)

DirectX::XMFLOAT3 Lerp(const DirectX::XMFLOAT3& start, const DirectX::XMFLOAT3& end, float t) {
    return {
        start.x + t * (end.x - start.x),
        start.y + t * (end.y - start.y),
        start.z + t * (end.z - start.z)
    };
}

Spherical Linear Interpolation (SLERP)

DirectX::XMFLOAT4 Slerp(const DirectX::XMFLOAT4& start, const DirectX::XMFLOAT4& end, float t) {
    DirectX::XMVECTOR startVec = DirectX::XMLoadFloat4(&start);
    DirectX::XMVECTOR endVec = DirectX::XMLoadFloat4(&end);
    DirectX::XMVECTOR resultVec = DirectX::XMQuaternionSlerp(startVec, endVec, t);
    DirectX::XMFLOAT4 result;
    DirectX::XMStoreFloat4(&result, resultVec);
    return result;
}

Implementing Animation in DirectX

To implement animation in DirectX, follow these steps:

  1. Load Keyframe Data: Load the keyframe data for the model.
  2. Calculate Interpolation: Interpolate between keyframes based on the current time.
  3. Update Model Transform: Apply the interpolated transformation to the model.

Example Code

void UpdateAnimation(float currentTime) {
    // Find the two keyframes to interpolate between
    Keyframe startKeyframe, endKeyframe;
    for (size_t i = 0; i < keyframes.size() - 1; ++i) {
        if (currentTime >= keyframes[i].time && currentTime <= keyframes[i + 1].time) {
            startKeyframe = keyframes[i];
            endKeyframe = keyframes[i + 1];
            break;
        }
    }

    // Calculate the interpolation factor
    float t = (currentTime - startKeyframe.time) / (endKeyframe.time - startKeyframe.time);

    // Interpolate position, rotation, and scale
    DirectX::XMFLOAT3 interpolatedPosition = Lerp(startKeyframe.position, endKeyframe.position, t);
    DirectX::XMFLOAT4 interpolatedRotation = Slerp(startKeyframe.rotation, endKeyframe.rotation, t);
    DirectX::XMFLOAT3 interpolatedScale = Lerp(startKeyframe.scale, endKeyframe.scale, t);

    // Update the model's transformation matrix
    DirectX::XMMATRIX translationMatrix = DirectX::XMMatrixTranslationFromVector(DirectX::XMLoadFloat3(&interpolatedPosition));
    DirectX::XMMATRIX rotationMatrix = DirectX::XMMatrixRotationQuaternion(DirectX::XMLoadFloat4(&interpolatedRotation));
    DirectX::XMMATRIX scaleMatrix = DirectX::XMMatrixScalingFromVector(DirectX::XMLoadFloat3(&interpolatedScale));

    DirectX::XMMATRIX worldMatrix = scaleMatrix * rotationMatrix * translationMatrix;
    model.SetWorldMatrix(worldMatrix);
}

Practical Exercises

Exercise 1: Basic Keyframe Animation

Task: Create a simple animation where a cube moves from one position to another over 5 seconds.

Steps:

  1. Define keyframes for the cube's position.
  2. Implement linear interpolation for the position.
  3. Update the cube's transformation matrix based on the interpolated position.

Solution:

std::vector<Keyframe> cubeKeyframes = {
    {0.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}},
    {5.0f, {5.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}}
};

void UpdateCubeAnimation(float currentTime) {
    // Find the two keyframes to interpolate between
    Keyframe startKeyframe, endKeyframe;
    for (size_t i = 0; i < cubeKeyframes.size() - 1; ++i) {
        if (currentTime >= cubeKeyframes[i].time && currentTime <= cubeKeyframes[i + 1].time) {
            startKeyframe = cubeKeyframes[i];
            endKeyframe = cubeKeyframes[i + 1];
            break;
        }
    }

    // Calculate the interpolation factor
    float t = (currentTime - startKeyframe.time) / (endKeyframe.time - startKeyframe.time);

    // Interpolate position
    DirectX::XMFLOAT3 interpolatedPosition = Lerp(startKeyframe.position, endKeyframe.position, t);

    // Update the cube's transformation matrix
    DirectX::XMMATRIX translationMatrix = DirectX::XMMatrixTranslationFromVector(DirectX::XMLoadFloat3(&interpolatedPosition));
    DirectX::XMMATRIX worldMatrix = translationMatrix;
    cube.SetWorldMatrix(worldMatrix);
}

Exercise 2: Rotating and Scaling Animation

Task: Create an animation where a sphere rotates and scales over 3 seconds.

Steps:

  1. Define keyframes for the sphere's rotation and scale.
  2. Implement SLERP for rotation and LERP for scale.
  3. Update the sphere's transformation matrix based on the interpolated rotation and scale.

Solution:

std::vector<Keyframe> sphereKeyframes = {
    {0.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}},
    {3.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, {2.0f, 2.0f, 2.0f}}
};

void UpdateSphereAnimation(float currentTime) {
    // Find the two keyframes to interpolate between
    Keyframe startKeyframe, endKeyframe;
    for (size_t i = 0; i < sphereKeyframes.size() - 1; ++i) {
        if (currentTime >= sphereKeyframes[i].time && currentTime <= sphereKeyframes[i + 1].time) {
            startKeyframe = sphereKeyframes[i];
            endKeyframe = sphereKeyframes[i + 1];
            break;
        }
    }

    // Calculate the interpolation factor
    float t = (currentTime - startKeyframe.time) / (endKeyframe.time - startKeyframe.time);

    // Interpolate rotation and scale
    DirectX::XMFLOAT4 interpolatedRotation = Slerp(startKeyframe.rotation, endKeyframe.rotation, t);
    DirectX::XMFLOAT3 interpolatedScale = Lerp(startKeyframe.scale, endKeyframe.scale, t);

    // Update the sphere's transformation matrix
    DirectX::XMMATRIX rotationMatrix = DirectX::XMMatrixRotationQuaternion(DirectX::XMLoadFloat4(&interpolatedRotation));
    DirectX::XMMATRIX scaleMatrix = DirectX::XMMatrixScalingFromVector(DirectX::XMLoadFloat3(&interpolatedScale));
    DirectX::XMMATRIX worldMatrix = scaleMatrix * rotationMatrix;
    sphere.SetWorldMatrix(worldMatrix);
}

Conclusion

In this section, we covered the basics of animating 3D models in DirectX. We learned about keyframe animation, interpolation techniques, and how to implement these concepts in DirectX. By completing the practical exercises, you should now have a solid understanding of how to animate 3D models in your DirectX applications. In the next section, we will delve into skeletal animation, which is a more advanced and flexible animation technique.

© Copyright 2024. All rights reserved