스키닝 애니매이션이란?
스키닝 애니메이션(Skinning Animation)은 3D 컴퓨터 그래픽에서 객체가 자연스럽게 움직이도록 하기 위해 사용하는 애니메이션 기법이다. 3D 모델의 뼈대를 기반으로 정점을 뼈대에 결합시키는 기법이다.
이렇게 개념만 보면 무슨 말인지 모르겠다.
그래서 본인이 스스로 공부한 것을 바탕으로 최대한 이해하기 쉽게 정리해보려고한다.
(렌더링 파이프라인과정과 좌표계 변환 개념을 잘 이해하지 못하고 있다면 어렵다)
모델이란?
모델은 여러개의 메쉬들로 이루어진 경우가 많기 때문에 본인은 모델의 정의를 '여러 메쉬들로 이루어진 메쉬의 집합'라 하겠다.
그리기의 단위는 점, 선, 삼각형 순인데 정점 3개가 모이면 '폴리곤(polygon)'이라 부르고 이 폴리곤의 집합을 '메쉬(mesh)'라고 한다.
각 폴리곤에 해당하는 정점들은 위치, 방향 등 여러 정보를 담고 있고 이 폴리곤에 해당하는 정점 정보들을 쉐이더 바인딩하게 되면 월드 변환, 뷰 스페이스 변환, 투영 변환이 이루어져 화면에 렌더링 되게 된다.
근데 왜 눈에 보이는 모델은 하나인데 왜 여러개의 메쉬라고 하는것인가?
실제로 Assimp라이브러리를 통해 모델을 로드하게 되면 여러개의 메쉬들로 로드가 되는 경우가 많은데(아닌 경우도 있다)
RGP게임류들의 캐릭터를 보면 칼을 차거나, 망토를 두르거나 특정 부분(팔, 다리, 등)에 다른 메쉬들을 착용하는 경우가 많다. 모델이 여러개의 메쉬들로 이루어져 있다면 특정 메쉬의 특정 파츠에 붙이거나 때는 작업등이 수월해지기 때문에 나누어 놓았다고 볼 수 있다.
스키닝 애니매이션
그럼 애니매이션의 목표는 여러개의 메쉬들로 이루이진 모델의 정점을 프레임 단위로 움직여 줘야 사람이 움직이는 듯한 느낌을 줄 수 있을 것이다.
'아 그럼 프레임 단위로 정점을의 위치를 다 저장해 놓으면 어떨까?' 라고 생각 한다면 맞는 말이다. 된다!
근데 보통 3D모델 데이터는 .fbx 확장자로 관리하게 되는데 모델의 정점이 10만개라고 쳤을 때 이 10만개에 대한 정점 위치, 회전 정보등을 프레임별로 fbx파일에 저장하게 되면 파일 용량이 어마어마하게 커질 것이다.
또한 fbx파일을 로드하는데에도 엄청난 시간이 걸릴 것이다.
뭔가 해결 방법이 필요하다. 이 문제의 해결 방법으로 '스키닝 애니매이션' 이라는 기법이 있다.
팔을 이렇게 움직이는 애니매이션이 있다고 하자, 그리고 팔을 움직이는 모델안에 사람의 팔 뼈가 있다고 생각을 하자.
우리는 모든 정점들에 대한 위치 정보들을 프레임 별로 저장하지 않고 가상의 '뼈'를 만들어서 이 뼈의 위치, 회전 정보들을 프레임 별로 저장하고 각 정점에게 이 뼈의 정보를 넘겨준뒤 각 정점들은 뼈로 부터 상대적인 위치를 잡아주게 되면 뼈가 움직이게 되면 정점들도 뼈를 따라서 움직이게 될 것이다.
위 모델은 건파이어 리본(고양이가 총 쏘는 1인칭 게임이다) 모작하면서 Assimp로 로드한 fbx파일인데 위 모델을 기준으로 설명을 하겠다.(총 3개의 메쉬들로 이루어진 모델이다)
팔만 뚝 때와서 관차을 해보도록 하자. 위 모델이 렌더링 되는 이유는 뼈의 상대적인 위치 정보를 담아 두고 이 상대적인 위치정보를 바탕으로 월드, 뷰, 투영 변환을 거쳐서 렌더링 된것이다.
따라서 각 정점은 A, B, C, D모두 자신이 따라가야할 뼈의 정보를 가져야 한다. (여기서는 뼈 정보를 인덱스라 표현하겠다)
Assimp에서는 정점 하나당 최대 4개의 뼈 인덱스 정보를 가질 수 있다.
그 이유는 정점 A를 보도록 하자. (정점 A는 현재 팔꿈치에 있는 정점이라 하자)
팔을 몸 안쪽으로 굽히는 애니매이션을 동작 시킬 때 1번 본이 움직이면서 아래처럼 A정점의 위치가 변경될 것이다.
일부로 좀 찌그러지게 그렸는데 팔꿈치나 관절에 해당하는 정점에 자신이 따라가야할 뼈 정보를 하나만 넘겨주게 되면 진짜 메쉬가 찌그러진다.
그래서 한 정점당 따라갈 수 있는 본을 최대 4개로 두고 정점은 각 뼈에 대해 '가중치(weights)' 라는 것을 두어 A 정점은 1번뼈에 대한 가중치를 0.5를 주고 0번 뼈에 대한 가중치도 0.5를 주고 1번 뼈가 움직일 때 100%다 A정점도 움직이는게 아니라 50%정도만 움직이게 하여 메쉬의 찌그러짐? 을 해결 할 수 있다.
메쉬의 특정 부분이 늘어나있는 것을 확인할 수 있다. 이게 거의 보통 가중치값이 잘 못 되어 있는경우 모델의 메쉬가 이렇게 늘어나서 괴상하게 보이는 경우가 많다.
이게 정상적인 경우다. 지금 텍스쳐가 없어서 팔에 텍스쳐가 입혀져 있지는 않지만 가중치 값이 제대로 설정되어 있다면 메쉬가 늘어나거나 깨지는 현상은 없다.
만약 늘어난다면 fbx로드하는 과정에서 값이 잘 못 들어가거나 fbx파일 자체의 문제일 가능성이 크다.
그런데 각 정점들의 뼈에 대한 '상대적인 위치 정보'들로 어떻게 월드 상에 표현되어 렌더링 되는 것일까?
이것을 이해하기 위해서 좌표계 변환에 대해 이해하고 있어야 이해가 가능하다.
스키닝된 정점 렌더링 하기
fbx파일을 Assimp로 로드하게 되면 우리는 가장 먼제 모델의 '뼈'를 계층 구조로 로드해준다. 왼쪽은 특정 모델의 뼈 구조이고 오른쪽은 이 뼈의 계층 구조를 트리의 형태로 나타낸 것이다.
보통 머리통이 0번 뼈(루트 뼈)가 되지는 않는데 편의상 머리통뼈가 루트라고 가정하겠다.
이 루트의 자식으로 1, 2, 3번 뼈가 들어가고 1, 2, 3번도 각각 자식의 뼈를 가진다. 4번 뼈의 경우 부모를 1번 뼈를 가지는데 1번 뼈를 움직이면 4번, 5번도 움직인다는 것이다. 근데 이게 우리 어깨만 움직여 봐도 안다. 오른쪽 어깨를 막 돌리면 오른쪽 팔이 다 돌아간다. 왼쪽팔이 돌아가지는 않는다. (너무 당연하다)
여기서 '행렬'이 쓰인다. 개인적으로 아주아주 중요하다고 생각한다.
보통 DX를 공부할 때 SRT (Scale * Rotation * Translation) 순으로 곱한 행렬이 '월드 변환' 행렬이라고 알고 있을 텐데 여기에 '부모 행렬'을 곱해주면 부모 좌표계를 기준으로한 좌표가 나오게 된다.
간단한 예를 들어 보도록 하자.
A가 부모이고 B가 자식이라고 하고 부모 A의 월드 변환 행렬과 B의 월드 변환 행렬이 있다.
A는 회전이 없고, 스케일도 1인 SR이 항등이라 하고 B도 마찬가지로 SR이 항등이라고 치자.
B는 부모를 A로 가지므로 B의 SRT(월드 변환 행렬) * A의 월드 변환 행렬 을 곱하면
B의 월드 위치는 (7, 3, 0)이 나오게 된다.
이것을 풀어서 설명하면 B는 A로 부터 상대적인 위치는 (2, 3, 0)만큼 떨어져 있고 B의 월드 위치는 부모인 A의 월드 행렬을 곱하면 (7, 3, 0)으로 구할 수 있다는 뜻이 된다.
마찬가지로 뼈들도 이런 자신의 로컬 스페이스 상의 위치정보들이 다 있다. 1번 뼈(오른쪽 팔)의 경우에도 0번 뼈를 기준으로 얼마 만큼 떨어져 있는지에 대한 정보가 있고 4번 뼈도 마찬가지로 이러한 정보들이 있다.
이 뼈들의 월드 위치를 구하기 위해서는 '더 이상 부모가 없을 때 까지 부모의 월드 변환 행렬을 곱해주면 자신의 월드 위치를 알 수 있다.'
그럼 정점 A의 월드 위치도 알 수 있다. 정점 A도 1번 뼈로 부터 얼마만큼 떨여져 있는지 상대적인 위치 정보가 있을 태니 1번뼈의 월드 변환 행렬에 정점 A의 로컬 스페이스 행렬을 곱해주면 정점 A의 월드 위치를 알 수 있다.
이다음에는 당연히 뷰 스페이스 변환 행렬 * 투영 변환 행렬을 하면 최종적으로 화면에 렌더링 되는 것이다.
정리
애니매이션은 그야말로 DX개념의 총 집합인거 같다. 행렬, 좌표계 변환 개념, 렌더링 과정 등 많은 부분들에 대해 알고 있어야 하는듯 하다.
위에서 설명한 스키닝 애니매이션 개념과 렌더링되는 과정을 정말 짧고 정말 기초에 해당하는 개념들만 본인이 공부해서 정리한 내용이다. 빼먹은 부분이 많을 수 있고 조금 잘 못된 부분이 있을 수 있다. (더 자세한 설명은 아래 고려대 그래픽스 강의를 수십번 보는 것을 추천한다.)
애니매이션 이해하기가 본인은 처음에 좀 어려웠는데 나중에는 그래도 기초 개념에 대해서 이해할만 했던 이유가 좌표계 변환의 개념과 의미를 어느정도 이해하고 있어서 그나마 이해가 되었던거 같다.
무튼 정리하면 게임에서는 '실시간 렌더링'을 하기 때문에 스켈레탈(뼈)의 개념을 넣어서 정점들이 특정 뼈를 기준으로 상대적인 위치, 가중치를 가지게 하고 이 뼈도 부모를 기준으로 한 위치 정보, 회전 정보등을 가지게 하여 (매프레임) 애니매이션을 구현할 수 있다는 것이다.
참고 자료
https://www.youtube.com/watch?v=aZcrHIDO5zY&list=PLYEC1V9tJOl03WLDoUEKbiYW_Xt4W6LTl&index=14
'컴퓨터 그래픽스 > DirectX' 카테고리의 다른 글
NavMesh 만들기 (2) | 2024.12.07 |
---|---|
[Animation] 애니매이션을 만들어보기 (정리글) (1) | 2024.12.06 |
View Spcae Matrix 만들기 (0) | 2024.07.31 |
[DX] 렌더링 파이프 라인 (2) | 2023.11.30 |
[DX] Multi Sampling (0) | 2023.08.23 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!