ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 24. 애니메이션 ( Animation )
    Software/DirectX 2024. 5. 24. 10:57
    728x90

    Table of Contents

    1. 키 프레임(Key Frame)


    2. 뼈대 좌표(Bone coordinate)


    3. 오프셋 변환(Offset transformation)


    4. 뼈 애니메이션(Bone Animation)


    5. 정점 혼합(Vertex Blending)





    1. 키 프레임(Key Frame)
    • 키 프레임은 시간상의 어떤 순간에서의 물체의 위치와 방향, 축척을 지정하는 구조체이다.
    • 여러개의 키 프레임을 연속적으로 물체에 적용시켜 물체를 회전, 이동시키는 것이 키 프레임 애니메이션(Key Frame Animation)이다.

    key frame 구조체

    struct Keyframe
    {
       Keyframe();
       ~Keyframe();
    
       float TimePos;
       DirectX::XMFLOAT3 Translation;
       DirectX::XMFLOAT3 Scale;
       DirectX::XMFLOAT4 RotationQuat;
    };
    • 보통의 키 프레임은 아주 세세한 단위로까지의 위치정보까지 포함하지 않다. 한마디로 Discret한 형태의 움직임 정보를 가지고 있는 것이다.
    • 이를 조금 더 부드러운 움직임으로 바꾸기 위해서는 키프레임 사이의 값들을 일정한 크기로 보간해서 해당 보간 정점으로 위치를 변경해 나가는 것이다.
    • 기존의 애니메이션의 동작이 잘게 쪼개져서 훨씬 부드러운 움직임의 표현이 가능해진다.
    • 회전 보간을 위해서는 사원수(Quternium)의 자료형으로 회전 정보를 담아야한다.
    • 뼈 구조에서 루트 뼈(root bone)만이 세계 좌표에 들어가면 나머지 하위 본들은 그에 맞춰 움직인다.
    • 하위 뼈들의 관절(Joint)은 상위 뼈와 만나는 한점(pivot)과 연결되어 있으며 자신만의 로컬 좌표계의 원점에 위치한다.






    2. 뼈대 좌표(Bone coordinate)
    • 움직이는 사물들은 모두 일정한 Hierarchy를 가진 뼈대 계통구조를 이루어 움직이게된다.
    • 예를 들어 인간같은 이족보행 개체의 경우 골반(root)가 움직이면 허벅지, 하부 척추, 상부 척추, 손 까지 계층구조로 움직이게 된다.
    • 따라서 자신보다 상위에 있는 부모 뼈대가 움직이면 자식 뼈대들도 따라 움직이게 되는 것이다.
    • 뼈대 계층 구조에서는 뼈대의 키프레임을 하향식으로 변환시켜주는 것이 효율적이다.
    • 상향식으로 변환행렬을 적용하면 하부 뼈대는 항상 뿌리까지 변환행렬을 구한 뒤 세계변환 시켜야 하지만, 하향식으로 변환을 적용하면 항상 뼈대가 가지고 있는 뿌리변환행렬 하나만 있으면 되기 때문이다.






     

    3. 오프셋 변환(Offset transformation)
    • 위의 뼈 애니메이션에서 한가지 문제점이 있다.
    • 뼈마다 붙은 스킨(skin)정점들이 영향을 받는 각 로컬 뼈좌표계에 있는 것이 아니라 처음에 메쉬를 만든 전체 메쉬 좌표계 상에 위치해 있다는 것이다.
    • 따라서 뼈대 애니메이션을 사용하려면 각 정점을 자신이 영향받는 뼈대의 좌표계로 분류하는 작업을 해야한다 이것을 오프셋 변환이라고 한다.
    • 뼈대의 오프셋 변환과 뿌리 변환행렬을 결합 한 것을 최종 변환(final transforamtion)이라고 부른다.

    i번째 뼈대의 최종 변환

        Fi = 오프셋변환i + 뿌리변환i







     

     

    4. 뼈 애니메이션(Bone Animation)
    • 본격적인 뼈 애니메이션 구현에 앞서 캐릭터의 본 에니메이션들을 애니메이션 별로 가지고 있는 애니메이션 클립(Animation Clip)객체를 구현한다.
    struct AnimationClip
    {
     // 이 클립의 모든 뼈대 중 가장 이른 시작 시간을 돌려준다.
     float GetClipStartTime()const;
     // 이 클립의 모든 뼈대 중 가장 늦은 종료 시간을 돌려준다.
     float GetClipEndTime()const;
     // 이 클립의 각 BoneAnimaion을 훑으면서 애니메이션을 보간한다.
     void Interpolate(float t, std::vector<DirectX::XMFLOAT4X4>& boneTransforms)const;
     // 이 클립을 구성하는 뼈대 애니메이션들
     std::vector<BoneAnimation> BoneAnimations;     
    };
    • 보통의 캐릭터 골격 구조는 뼈의 부분마다 내려가는 트리형태의 구조를 띈다.
    • 애니메이션 클립에서 뼈대의 인스턴스 색인과 일치하는 오프셋 색인을 불러와 최종 변환을 계산하는 매서드이다.
    void SkinnedData::GetFinalTransforms(const std::string& clipName,
        float timePos,  std::vector<XMFLOAT4X4>& finalTransforms)const
    {
    
     UINT numBones = mBoneOffsets.size();
    
     std::vector<XMFLOAT4X4> toParentTransforms(numBones);
    
    
     // 이 클립의 모든 뼈대를 주어진 시간(순간)에 맞게 보간한다.
     auto clip = mAnimations.find(clipName);
     clip->second.Interpolate(timePos, toParentTransforms);
    
    
     // 골격 구조를 훑으면서 모든 뼈대를 뿌리 공간으로 변환한다.
     std::vector<XMFLOAT4X4> toRootTransforms(numBones);
    
     // 뿌리뼈대의 색인은 0이다.
     // 뿌리 뼈대의 뿌리 변환은 자신의 로컬 뼈대 변환이다.
     toRootTransforms[0] = toParentTransforms[0];
    
    
     // 자식 뼈대들의 뿌리 변환을 구한다.
     for(UINT i = 1; i < numBones; ++i)
     {
      XMMATRIX toParent = XMLoadFloat4x4(&toParentTransforms[i]);
      int parentIndex = mBoneHierarchy[i];
      XMMATRIX parentToRoot = XMLoadFloat4x4(&toRootTransforms[parentIndex]);
      XMMATRIX toRoot = XMMatrixMultiply(toParent, parentToRoot);
      XMStoreFloat4x4(&toRootTransforms[i], toRoot);
     }
    
    
     // 뼈대 오프셋 변환을 먼저 곱해서 최종 변환을 구한다.
     for(UINT i = 0; i < numBones; ++i)
     {
      XMMATRIX offset = XMLoadFloat4x4(&mBoneOffsets[i]);
      XMMATRIX toRoot = XMLoadFloat4x4(&toRootTransforms[i]);
      XMMATRIX finalTransform = XMMatrixMultiply(offset, toRoot);
      XMStoreFloat4x4(&finalTransforms[i], XMMatrixTranspose(finalTransform));
     }
    }







     

     

    5. 정점 혼합(Vertex Blending)
    • 뼈를 부분별로 움직이는 애니메이션을 수행하였으면 다음 차례는 뼈에 붙은 정점들을 뼈의 위치에 옮기는 것이다.
    • 정점 메쉬들은 경계로 구분되어 있는 것이 아니기 때문에 한 정점이 여러 뼈에 영향을 받을 수도 있다.
    • 여러 뼈에 영향을 받는 정점은 해당 뼈들의 위치를 보간해서 가중 평균으로 계산한다.
    • 실제 게임에서는 하나의 정점에 영향을 주는 뼈대의 갯수는 4개정도면 충분하다.
    • 따라서 메쉬의 각 정점에는 최대 4개정도의 뼈 색인 정보가 담겨있으며 이를 보간해서 정점 위치를 계산할 수 있다.
    • 또한 각 정점은 각각의 뼈 색인이 어느정도의 영향을 주는 지 가중치에 대한 정보도 지니고 있다.
    • 이렇게 뼈 색인과 가중치를 가지고 있는 정점 메쉬를 스킨 메쉬(Skinned mesh)라고 부른다.

    뼈대 가중치와 뼈 색인을 담은 바뀐 정점 구조체

     struct PosNormalTexTanSkinned
     {
         XMFLOAT3 Pos;       // 위치
         XMFLOAT3 Noraml;    // 법선
         XMFLOAT2 Tex;       // 텍스쳐
         XMFLOAT4 TangentU;  // 회전
         XMFLOAT3 Weights;   // 가중치
         BYTE BoneIndices[4];// 뼈 색인
     }

     

    스킨 메쉬가 4개의 뼈 색인 정보(wo,w1,w2,w3)을 가진다고 할 때,
    정점 v의 blend 정점 v'는 다음과 같다.

     // F는 각 뼈대의 가중치
     v' = w0vF0 + w1vF1 + w2vF2 + w3vF3 
    • 위와 같은 계산으로 메쉬의 피부들을 뼈대의 에니메이션에 맞게 적정한 위치로 옮길 수 있다.
    • 각 정점이 항상 4개의 뼈에 영향을 받을 필요는 없다. 영향받지 않으면 가중치 0으로 갯수 줄여나가면 된다.






     

    728x90