✅ 1. 2D vs 3D 애니메이션의 차이점
- 2D 애니메이션에서는 프레임마다 스프라이트(이미지)를 틀어주는 방식으로 애니메이션을 구현한다.
- 하지만 3D에서는 각 프레임마다 모델 전체를 교체하는 것은 비효율적이다. 대신 뼈대(Bone)를 기준으로 정점을 변형시키는 방식이 사용된다.
- 즉, 정점(Vertex)은 고정되어 있고, 이를 이동시키는 기준점으로서의 뼈대가 존재한다.
▶️ 본의 역할
- 본(Bone)은 캐릭터의 뼈대를 의미한다. 하나의 본은 캐릭터의 한 부분(예: 팔, 다리 등)을 의미하며, 계층적으로 연결되어 있다.
- 부모 본의 움직임은 자식 본에 영향을 준다. 예: 어깨를 움직이면 팔과 손도 함께 따라 움직인다.
- 각 본은 Local(자기 기준) 좌표를 갖고 있고, 이들이 SRT(Scale, Rotation, Translation)를 거쳐 Global(전역) 좌표로 바뀌게 된다.
- 기본 포즈인 T-Pose는 Global 좌표 상태에서 시작된다.
- 이후 애니메이션 데이터에 따라 본들의 Local Transform이 변형되며 최종 Global Transform이 재계산된다.
✅ 3. 글로벌 <-> 로컬 좌표 변환의 핵심
이 개념이 굉장히 중요하다. 아래 과정을 머릿속에 새겨두자:
T-Pose(Global) → Relative(Local) → 애니메이션 적용(Global)로 변환
- Global → Local : 정점을 뼈대 기준으로 변환 (역행렬 사용)
- Local → New Global : 애니메이션 데이터를 기반으로 새로운 Global 좌표를 계산
이 구조를 통해 뼈대가 애니메이션으로 움직이면, 정점이 그에 따라 자연스럽게 움직이게 된다.
✅ 4. 정점(Vertex)과 스키닝(Skinning)
▶️ 정점은 고정된 위치를 가진 점이 아니다
- 애니메이션은 뼈대만 움직이는 게 아니라, 정점들도 본의 움직임에 따라 변화해야 한다.
- 하지만 정점 하나가 여러 본에 영향을 받을 수 있다. 이때 사용하는 기법이 바로 스키닝(Skinning)이다.
▶️ 스키닝(Skinning)
- 하나의 정점은 최대 4개의 본에 영향을 받을 수 있다.
- 각 본은 가중치(Weight)를 가진다. 예: 가슴 정점은 허리 60%, 가슴 40% 본에 영향을 받는다.
- 최종 위치는 이들 본의 변환행렬에 가중치를 곱해서 더한 값이다.
예시:
finalPos = (BoneMatrix[0] * weight0) + (BoneMatrix[1] * weight1) + ...
이 방식으로 정점 하나가 여러 본의 움직임을 혼합해서 따라가게 된다.
✅ 5. 애니메이션 데이터 구조
▶️ 애니메이션 클립(Animation Clip)
- 하나의 동작 (예: 걷기, 뛰기, 공격 등)에 해당하는 시간별 본의 위치/회전/스케일 변화 데이터를 담고 있음.
- 매 프레임마다 본의 Local Transform을 변화시켜 애니메이션을 구현함.
✅ 6. 쉐이더와 애니메이션 적용
📌 HLSL의 핵심 코드
output.position = mul(input.position, BoneTransforms[BoneIndex]);
output.position = mul(output.position, W);
output.position = mul(output.position, VP);
📌 데이터 흐름
- 정점 정보는 모델 로딩 시 Global 좌표로 주어짐
- T-Pose 기준 Global Transform을 역행렬로 변환 → Local 좌표계로 변환
- 애니메이션 본 Transform을 계산하여 → 다시 Global로 재변환
- 최종 본 Transform에 따라 정점 위치 보정 (스키닝 적용)
- 쉐이더에서 최종 위치 계산 후 렌더링
✅ 8. 캐릭터 애니메이션 흐름
- 모델을 로딩하면서 본(Bone) 구조와 정점(Vertex) 정보를 가져온다
- 정점마다 영향을 주는 본의 인덱스와 가중치를 저장해둔다 (BlendIndices / BlendWeights)
- 애니메이션 클립을 로딩하여 본의 시간별 위치 데이터를 저장
- 매 프레임마다 본의 위치 정보를 계산 → BoneTransforms에 저장
- 쉐이더에서 각 정점의 본 인덱스를 기준으로 BoneTransforms를 적용해 최종 위치 계산
- 자연스럽게 애니메이션이 동작!