Skeletal Animation(뼈대 애니메이션)

1. Skeletal Animation



DXUT를 이용하여 그래픽스를 공부하다보면 다른 내용은 수월하게 넘어가는 편이지만 의외로 애니메이션만큼은 내용도 난해하고 이해하기가 어렵습니다. 다른 예제는 그저 예제따라 변수 설정 몇 개 해주고 스테이트 설정하거나 함수를 인자에 맞게 돌려주면 구동되는 반면에 애니메이션만큼은 추상 클래스부터 구체화 시킨 뒤에 또 별별 세팅을 다 해줘야하는데 그 참조할만한 문헌은 이해도 잘 안 되고 난해하기까지 합니다. ( 물론 봤더니 이해할만하더라 하시는 분은 가볍게 넘겨주시면 되겠습니다. ) 결국 DirectX Sample에 있는 SkinnedMesh를 이해하는 데 거의 한 달 가까이 걸렸습니다. 그래서 다른 분들은 이 삽질을 피해갈 수 있었으면 하는 바, 간단하게나마 이론적 배경과 실제 적용에 대해서 설명하려고 합니다.

일단 애니메이션에 대한 이해를 위해서는 다음과 같은 용어를 잘 파악해야 합니다.


D3DX 라이브러리에서 지원하는 것은 뼈대 애니메이션(Skeletal Animation)으로 키프레임(Keyframe) 방식을 구현하고 있습니다.

  • 뼈대 애니메이션이란 보이는 물체 뒤에 뼈(Bone)를 넣어서 뼈의 움직임을 통해 애니메이션을 구현하는 방식

  • 키프레임 방식이란 애니메이션의 특정한 동작, 즉 키프레임들만을 지니고 그 동작 사이사이는 적당히 보간(Interpolation)하여 표현하는 방식.


보간이란 앞 뒤의 몇몇 포인트로부터 그 사이 임의의 포인트를 유추하는 것입니다.
(자세한 설명은 범위를 너무 벗어나는 관계로 생략하겠습니다.)

아래에서는 뼈대 애니메이션과 키프레임 방식에 대해서 설명하고
이것이 D3DX에서는 어떻게 적용되어 돌아가는지 설명하도록 하겠습니다.




<<<<뼈대 애니메이션>>>


뼈대는 그래프다!!!!!!!!!!

뼈대 애니메이션에서 뼈대(Skeleton)는 대체 무엇일까요?
뼈대는 뼈(Bone)의 조합입니다.
그렇다면 뼈대를 구성하는 는 무엇일까요?
 [명사] <의학>척추동물의  에서  을 지탱하는 단단한 물질.

네. 그렇습니다. 우리의 몸의 형태를 유지시켜주는 단단한 지지대의 하나하나의 구성물입니다.
그렇다면 그 뼈의 특징은 무엇일까요?

  1. 단단합니다. 휘지 않죠. 항상 곧은 형태를 유지합니다. 조금 추상화시켜 생각한다면 이라고 볼 수 있겠네요.
  2. 양 끝에 다른 뼈와 연결되어 있습니다. 각 이음새(Joint)가 연결된 상태를 유지합니다. 실제로는 이음새는 연골이라는 특수한 물질로 이루어져 있겠지만, 여기서는 추상화시켜 이라고 표현합시다.

혹시 뭔가 떠오르시나요? 점(Vertex)과 선(Node)으로 이루어지고 점과 점 사이에는 선이 항상 존재하는 무언가.

graph.jpg

네. 바로 그래프(Graph)입니다. 점과 점 사이가 선으로 연결되어 있는 자료구조죠.

위에서 언급한 뼈대를 이렇게 설명해 놓은 의미에 하나씩 대입을 해보겠습니다.
  • 점 : 틀(Frame)이라고 합니다. 이 틀은 일종의 멀티탭으로 뼈를 몇 개든 끼울 수 있습니다.
  • 선 : 뼈(Bone)라고 합니다. 물체와 물체 사이의 관계를 나타냅니다.
  • 그래프 : 뼈대(Skeleton)라고 합니다. 연결되어 있는 모든 틀과 뼈 한 뭉치를 하나로 봅니다.

자 이렇게 해서 위의 그림과 같은 구조를 만들고 나면 이제 뼈 위에다가 살점을 붙이는 겁니다.
1번 틀을 놓고 뼈를 부착한 뒤에 반대 쪽에 2번 틀을 끼우고 거기에 살점을 바르는 것이죠.
그리고 우리들 눈에는 그 살점만이 보이게되는 것입니다.


뼈대는 트리다!!!!!!!!!!!!

뼈대는 그래프 중에서도 트리(Tree)입니다.
트리란, 하나의 근원이 되는 뿌리(Root)를 가지고 있는 비순환(Acyclic) 그래프를 말합니다.
tree.jpg

위의 그래프가 바로 트리입니다. Encyclopeaedia를 뿌리로 하는 트리이죠. 
한 점에서 출발하여 지나간 길을 통하지 않고는 다시 그 점으로 돌아올 수 없을 때 비순환 그래프라도 합니다.
만일 위의 예제에서 Art와 Craft가 연결되어 있었다면 위 그래프는 순환(cyclic) 그래프인 것이죠.
참고로 트리에서 선으로 연결된 두 점의 관계를 설명할 때에
뿌리에 가까운 쪽을 부모(Parent), 뿌리에서 먼 쪽을 자식(Child)이라고 합니다.
그리고 같은 부모의 자식들끼리는 형제(Sibling)라고 합니다.

트리에는 항상 뿌리가 있습니다.

뼈대에서 항상 뿌리인 틀이 존재합니다. 보통 기본틀(Root Frame)이라고 불리는 것이 그것입니다.
( 한글로 뿌리틀이라고 하긴 너무 어감이 아니군요. )
기본틀을 먼저 놔두고 그 틀에 뼈를 가져다 붙입니다.
그리고 가져다 붙인 뼈 반대쪽에 새로운 틀을 붙이고 뼈에는 살점을 붙입니다.
만약 그 틀 뒤에 또 붙일 뼈가 있다면 뼈를 붙이고 일련의 작업을 계속 합니다.
이 작업을 계속하면 하나의 뼈대가 탄생합니다.

이렇게 만들어진 뼈대는 마치 인체모형처럼 작용합니다.

하나의 틀이나 뼈를 움직이면 그 밑에 딸린, 즉 자식인 틀은 모두 영향을 받게 된다는 말입니다.
예를 들어보죠.
아래와 같은 뼈대가 있다고 합시다.
Torso.jpg
목(빨간 점)을 뿌리라고 한다면 그 뿌리인 틀에 뼈가 네 개( 목뼈, 오른어깨뼈, 왼어깨뼈, 가슴뼈 ) 붙어있죠.
오른어깨뼈 아래로는 윗팔뼈, 아랫팔뼈가 차례로 붙어있습니다.
프라모델이라고 생각하고 상상해보세요.
만일 오른팔꿈치를 위로 든다면?
오른팔에 해당하는 뼈는 모두 위로 움직이게 될 것이고 마치 만세를 부르는 형상이 될 것입니다.
오른어깨를 위로 들어올린다면?
오른어깨뼈는 위로 움직일 것이고 오른팔은 약간 더 벌어질 겁니다.
( 오른어깨뼈와 오른윗팔뼈의 각도가 유지되기 때문이지요. )
목을 앞으로 숙이면? 모든 뼈가 앞으로 숙여지겠죠.

이렇게 뼈대의 틀을 움직이면 그 자식에 해당하는 뼈와 틀도 따라서 움직이게 됩니다.

이게 바로 뼈대의 트리로서의 속성입니다.

그럼 위에서 알아본 뼈대로 그 구조를 어떻게 변경하는지 대충 감이 오시나요?

인체모형과 같은 뼈대를 만들어서 뼈와 틀의 움직임으로 물체의 움직임을 나타내는 것.
그것이 바로 뼈대 애니메이션입니다.




<<<<키프레임 방식>>>

키프레임 방식은 간단합니다.

1초에 30프레임짜리 동영상을 만든다고 합시다.
그러면 매 프레임을 일일이 보관하는 것이 아니라,
키(key)가 되는 프레임 - 이하 키프레임(keyframe) - 만을 보관해두고
나머지 프레임은 그 키프레임으로부터 유추하는 방식.
이것이 바로 키프레임 방식입니다.


예를 들면 아래와 같은 방식입니다.






처음 키프레임과 마지막 키프레임만을 지닌 채로
중간의 프레임을 그리게 되면 마지막 완성된 애니메이션이 나오게되는 겁니다.
그것을 보간한다(Interpolate)라고 하는데 그 보간하는 방법에 따라 여러 보간법으로 분류됩니다.
기본적으로 DirectX에서는 선형보간법( 두 키프레임 사이의 속도는 일정하다고 가정하는 방법 )하는 것으로 알고 있습니다.





D3DX에서의 애니메이션

자 그럼 이제 본론입니다. D3DX에서는 애니메이션을 어떻게 표현할까요?
표현 방법은 다음의 흐름을 따릅니다.

  1. 애니메이션 정보 읽어들이기
  2. 애니메이션 진행시키기
  3. 애니메이션 그리기
이 세 가지 내용을 아래에서 자세히 살펴보도록 하겠습니다.



1.애니메이션 정보 읽어들이기

DirectX 샘플에서는 SkinnedMesh를 제공하고 있습니다.
대체 Skinned가 뭘까 하시는 분이 있겠지만 일단 넘어가기로 하죠.
이 샘플을 살펴보면 D3DXLoadMeshHierarchyFromX()라는 함수를 이용해서 .x파일을 읽어들이고 있습니다.
( .x 파일은 DirectX Mesh File입니다. )
근데 이 함수는 ID3DXAllocateHierarchy라는 구조체를 인자로 달라고 하는데,
다른 인자와는 달리 눈을 씻고 찾아봐도 추상 클래스만 존재하지 구체 클래스는 보이질 않습니다.
여기가 바로 이 샘플로 애니메이션을 공부하시려는 분이 봉착하는 최초의 난관이 아닌가 싶습니다.


ID3DXAllocateHierarchy라는 구조체는 네가지 함수에 대한 인터페이스를 제공합니다.

MethodDescription
ID3DXAllocateHierarchy: : CreateFrame Requests allocation of a frame object.
ID3DXAllocateHierarchy: : CreateMeshContainer Requests allocation of a mesh container object.
ID3DXAllocateHierarchy: : DestroyFrame Requests deallocation of a frame object.
ID3DXAllocateHierarchy: : DestroyMeshContainer Requests deallocation of a mesh container object.

각각 D3DXFRAMED3DXMESHCONTAINER를 만들어 내고 없애는 함수를 가진 인터페이스입니다.
제가 위에서 구구장창 설명했든 개념은 바로 이것을 설명하기 위한 것입니다.
D3DXFRAME은 뼈대 애니메이션 항목에서 열심히 설명했던 입니다.
그리고 D3DXMESHCONTAINER는 사용자들에게 실제 보여주는 살점이죠.
어, 그럼 뼈대와 뼈는 어디있나요?
뼈대, 즉 트리는 각 D3DXFRAME 끼리 포인터를 통해 연결된 그 자체인 것이고,
뼈는 그저 개념일 뿐입니다.


자 뭐가 뭔지 헷갈리실 분들이 많을 것 같습니다.
뼈에 대해 조금 더 설명을 하자면,
뼈는 기본틀(Root Frame)을 제외한 모든 틀과 같다고 할 수 있습니다.
왜냐면 기본 틀을 제외한 모든 틀에는 그 부모와 연결되는 뼈 하나가 무조건 붙어있기 때문이죠.
아까 위의 그림을 다시 가져와서 보여드리면,
Torso.jpg
손에는 오른아래팔뼈가 붙어있고 팔꿈치에는 오른윗팔뼈가 붙어있습니다.
어깨에는 오른어깨뼈가 붙어있고, 오로지 목만이 부모와 연결되는 어떠한 뼈도 가지고 있지 않죠.
따라서 뼈는 틀과 동일하다고 봐도 무방합니다.
DIrectX에서는 두 용어를 모두 사용합니다.
보통 구조적인 것을 이야기할 때는 틀(Frame), 공간적인 관계를 이야기할 때는 뼈(Bone)를 씁니다.




그래서 다시 소스로 넘어가봅시다.
먼저 D3DXFRAME을 분석해보도록 하죠.

typedef struct _D3DXFRAME
{
   LPSTR                                   Name;                             // 이름
   D3DXMATRIX                         TransformationMatrix;    // 변환행렬
   LPD3DXMESHCONTAINER      pMeshContainer;           // 살점
   struct _D3DXFRAME              *pFrameSibling;            // 형제(트리)
   struct _D3DXFRAME              *pFrameFirstChild;        // 자식(트리)
} D3DXFRAME, *LPD3DXFRAME;


의 구조체에서 제일 중요한 것은 바로 변환행렬(Transformation Matrix, 줄여서 TM)입니다.
이 변환행렬은 바로 부모와 나와의 공간적 관계를 나타냅니다.
부모로부터 어느 방향으로 얼만큼 떨어져 있다고 나타내는 것이죠.
즉, 위에서 말한 부모와 연결된 뼈에 대한 내용이 바로 이 변환행렬입니다.
D3DXLoadMeshHierarchyFromX()를 이용하면 ID3DXAnimationController라는 구조체를 결과로 받을 수 있습니다.
이 구조체는 앞의 함수를 이용하여 만들어진 물체의 동작을 설정할 수 있는 클래스입니다.
이 클래스에서 AdvanceTime()으로 시간을 진행시키면 뼈대에서의 변환행렬이 그 때의 동작에 맞게 갱신됩니다.
저 함수를 이용하면 알맞은 보간을 통해 뼈를 움직여주고 그 결과를 위의 변환행렬에 반영해주죠.


자, 그 다음엔 D3DXMESHCONTAINER의 구조체를 지닌 살점을 알아봅시다.

그 구조엔 실제 정점버퍼를 지닌 ID3DXMesh와 그 외 여러 정보가 들어있습니다.
이 살점이 표현하는 곳은 바로 부모와 나 사이에 뼈 부분입니다.
어깨의 틀이 표현하는 부분은 바로 어깨와 그 부모인 목을 연결하는 어깨뼈의 부분을 표현하는 것이죠.
이 구조체의 구조를 설명하기 전에 스키닝(Skinning)이라는 개념을 이해해야 합니다.
하지만 이 개념은 차후에 설명하도록 하고 일단은 무엇을 나타내는 데 쓰이는 것만을 이해하도록 합시다.


그래서 ID3DXAllocateHierarchy의 구체 클래스를 완성해서 D3DXLoadMeshHierarchyFromX()에게 넘겨주면
뼈대는 기본틀(Root Frame)을 통해 접근하고,
동작은 ID3DXAnimationController를 통해 제어할 수 있습니다.
이것으로 기본적인 애니메이션 정보를 불러오는 과정은 일단락 된 것입니다.


애니메이션 진행시키기

애니메이션은 ID3DXAnimationController라는 구조체를 통해서 제어할 수 있습니다.
키프레임 방식으로 해당 프레임에 적절한 뼈대를 만들어 D3DXFRAME에 갱신해 줍니다.
위 구조체를 통해 여러 동작을 가지고 있을 때에 어떤 동작을 취하게 하든지, 몇 개의 동작을 섞는 등의 명령을 내릴 수 있습니다.
어려운 내용은 아니니 생략합니다.


애니메이션 그리기

이제 남은 것은 애니메이션을 그리는 것만 남았습니다.
기본틀부터 자신의 변환행렬을 월드 행렬(World Matrix)에 곱해가면서
틀에 속해있는 살점들을 하나하나 그려주면 되는 겁니다.
여기서 월드 행렬을 어떻게 곱하느냐에 따라서 SkinnedMesh 샘플에 나오는 여러가지 방식으로 갈리게 됩니다.
이 내용은 스키닝을 이해해야 설명할 수 있으므로 미뤄둡니다.


중간 마무리

이제 D3DX가 뼈대 애니메이션과 키프레임을 이용하여 어떻게 동작하시는지 아시겠나요?
안타깝게도 스키닝을 설명하기 전에 이 설명만 가지고는 샘플을 구현해볼 수가 없습니다.
그래도 여기까지 이해하셨다면 그리는 부분을 제외한 모든 부분을 이해하셨다고 해도 무방합니다.


다음 장에서는 스키닝과 함께 D3DX에 실제 구현하는 법을 설명하도록 하겠습니다.
스키닝을 읽기 전에 이 내용을 꼭 이해하도록 하세요.
각 용어의 개념, 그리고 뼈대의 구조, 그리고 그 구조가 그려지는 과정을 파악할 수 있으면 됩니다.

스키닝은 여력이 되는 대로 금방 올리도록 하겠습니다.


출저:::: Digitzetre 다섯번째 - 유入經로 ::: :: Animation with DirectX - 1. Skeletal Animation

이 글을 공유하기

댓글

Designed by JB FACTORY