PART 1 · 강의 2/3

본 계층과 소켓 시스템

Bone Hierarchy, Socket, Virtual Bone의 구조와 트랜스폼 전파 메커니즘을 깊이 있게 분석합니다.

SECTION 01

본 계층 구조(Bone Hierarchy)

부모-자식 관계로 구성되는 본 트리의 구조와 트랜스폼 전파

스켈레탈 메시의 본(Bone)은 부모-자식 트리 구조로 조직됩니다. 루트 본에서 시작하여 자식 본으로 트랜스폼이 전파되며, 각 본의 최종 위치는 부모 본의 트랜스폼에 자신의 로컬 트랜스폼을 곱하여 계산합니다.

일반적인 인간형 본 계층
root └── pelvis ├── spine_01 │ └── spine_02 │ └── spine_03 │ ├── neck_01 │ │ └── head │ ├── clavicle_l │ │ └── upperarm_l │ │ └── lowerarm_l │ │ └── hand_l │ └── clavicle_r │ └── upperarm_r │ └── lowerarm_r │ └── hand_r ├── thigh_l │ └── calf_l │ └── foot_l │ └── ball_l └── thigh_r └── calf_r └── foot_r └── ball_r
트랜스폼 스페이스

UE5에서 본 트랜스폼은 세 가지 스페이스로 표현됩니다: Bone Space(부모 본 기준 로컬), Component Space(컴포넌트 원점 기준), World Space(월드 좌표계). 애니메이션 노드 내부에서는 주로 Component Space를 사용하며, FCSPose<FCompactPose>로 표현합니다.

C++
// 로컬 스페이스 → 컴포넌트 스페이스 변환 FTransform LocalToComponent(const FReferenceSkeleton& RefSkel, const TArray<FTransform>& LocalTransforms, int32 BoneIndex) { FTransform CompTransform = LocalTransforms[BoneIndex]; int32 ParentIdx = RefSkel.GetParentIndex(BoneIndex); while (ParentIdx != INDEX_NONE) { CompTransform *= LocalTransforms[ParentIdx]; ParentIdx = RefSkel.GetParentIndex(ParentIdx); } return CompTransform; } // 컴포넌트 스페이스 → 월드 스페이스 FTransform WorldTransform = CompTransform * SkelMeshComp->GetComponentTransform();
SECTION 02

소켓(Socket) 시스템

본에 부착하는 가상 부착점 Socket의 구조와 활용법

USkeletalMeshSocket은 특정 본에 오프셋을 적용한 가상 부착점입니다. 무기, 이펙트, 카메라 등을 캐릭터 특정 위치에 부착할 때 사용합니다. 소켓은 USkeleton 또는 USkeletalMesh 레벨에서 정의할 수 있습니다.

속성 타입 설명
SocketName FName 소켓 고유 이름
BoneName FName 부착 대상 본 이름
RelativeLocation FVector 본 기준 위치 오프셋
RelativeRotation FRotator 본 기준 회전 오프셋
RelativeScale FVector 본 기준 스케일 오프셋
C++
// 소켓에 액터 부착 WeaponActor->AttachToComponent( CharacterMesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale, FName("weapon_r_socket")); // 소켓 월드 트랜스폼 가져오기 FTransform SocketTransform; bool bFound = SkelMeshComp->GetSocketTransform( FName("weapon_r_socket"), SocketTransform, ERelativeTransformSpace::RTS_World); // 소켓 존재 여부 확인 bool bHasSocket = SkelMeshComp->DoesSocketExist(FName("weapon_r_socket"));
Skeleton vs Mesh 소켓

Skeleton 소켓은 USkeleton 레벨에서 정의되어 해당 스켈레톤을 공유하는 모든 메시에 적용됩니다. Mesh 소켓은 특정 USkeletalMesh에만 적용됩니다. 같은 이름이 충돌하면 Mesh 소켓이 우선합니다. 일반적으로 범용 부착점(무기, 이펙트)은 Skeleton 소켓, 메시별 고유 위치는 Mesh 소켓으로 정의합니다.

SECTION 03

Virtual Bone

두 본 사이에 동적으로 생성되는 가상 본의 구조와 활용

Virtual Bone은 기존 두 본 사이에 동적으로 계산되는 가상의 본입니다. 실제 메시 데이터에 영향을 주지 않지만, IK 타겟이나 부착점으로 사용할 수 있어 리타겟팅이나 애니메이션 커스터마이징에 유용합니다.

IK 타겟 본

Hand IK, Foot IK의 이펙터 위치를 지정할 때 Virtual Bone을 사용하면, 원본 스켈레톤을 수정하지 않고 IK 타겟을 추가할 수 있습니다.

리타겟팅 보조

소스와 타겟 스켈레톤의 본 구조가 다를 때, Virtual Bone으로 중간 참조점을 만들어 리타겟팅 정확도를 높일 수 있습니다.

에이밍 참조

무기 에이밍 시 손 위치와 목표 지점 사이의 Virtual Bone을 만들어 Aim Offset이나 Look At의 참조점으로 활용합니다.

무기 부착점

양손 무기의 경우 왼손-오른손 사이에 Virtual Bone을 생성하여 무기 피벗 포인트를 동적으로 계산할 수 있습니다.

C++
// Virtual Bone 추가 (에디터 또는 코드에서) // USkeleton에서 관리됨 FName VBoneName = FName("VB hand_r_to_hand_l"); FName SourceBoneName = FName("hand_r"); FName TargetBoneName = FName("hand_l"); // Virtual Bone의 트랜스폼은 매 프레임 자동 계산: // Position = SourceBone.Position // Rotation = LookAt(SourceBone → TargetBone) // Scale = (1, 1, 1) // AnimBP에서 Virtual Bone을 IK 타겟으로 사용 // Modify Bone 노드로 Virtual Bone 위치를 오버라이드 가능
주의: Virtual Bone 제한

Virtual Bone은 스키닝에 영향을 주지 않습니다. 즉 버텍스 웨이트를 Virtual Bone에 할당할 수 없으며, 오직 트랜스폼 참조용으로만 사용됩니다. 과도한 Virtual Bone은 매 프레임 추가 트랜스폼 계산을 유발하므로, 필요한 경우에만 사용하세요.

SECTION 04

트랜스폼 전파와 본 매핑

FCompactPose, FBoneContainer를 통한 런타임 본 트랜스폼 처리

런타임 애니메이션 평가에서 본 트랜스폼은 FCompactPose로 표현됩니다. 이는 현재 LOD에서 필요한 본만을 포함하는 최적화된 포즈 컨테이너입니다.

클래스 용도 특징
FCompactPose 애니메이션 평가 결과 RequiredBones 기반 Compact 인덱싱, LOD별 최적화
FCSPose<FCompactPose> 컴포넌트 스페이스 포즈 IK 솔버 등에서 사용, 월드 기준 계산에 유리
FBoneContainer 본 인덱스 매핑 Skeleton 인덱스 ↔ Compact 인덱스 변환
FBlendedCurve 애니메이션 커브 모프 타겟, 머티리얼 파라미터 등 커브 데이터
C++
// 커스텀 AnimNode에서 FCompactPose 조작 void FAnimNode_CustomModify::Evaluate_AnyThread(FPoseContext& Output) { // 입력 포즈 평가 BasePose.Evaluate(Output); FCompactPose& Pose = Output.Pose; const FBoneContainer& RequiredBones = Pose.GetBoneContainer(); // 특정 본 트랜스폼 수정 FCompactPoseBoneIndex CompactBoneIndex = RequiredBones.GetCompactPoseIndexFromSkeletonIndex(TargetBoneSkeletonIndex); if (CompactBoneIndex != INDEX_NONE) { FTransform& BoneTransform = Pose[CompactBoneIndex]; // 로컬 스페이스에서 트랜스폼 수정 BoneTransform.SetRotation(BoneTransform.GetRotation() * AdditionalRotation); } }
SUMMARY

핵심 요약

  • 본 계층은 부모-자식 트리 구조로 구성되며, 트랜스폼은 루트에서 리프까지 전파된다.
  • 본 트랜스폼은 Bone Space(로컬), Component Space, World Space 세 가지로 표현된다.
  • Socket은 본에 오프셋을 적용한 부착점으로, Skeleton 레벨과 Mesh 레벨에서 각각 정의할 수 있다.
  • Virtual Bone은 두 본 사이에 동적 계산되는 가상 본으로, IK 타겟이나 리타겟팅 참조점으로 활용된다.
  • 런타임에서 본 트랜스폼은 FCompactPose로 관리되며, LOD에 따라 필요한 본만 포함하는 최적화된 컨테이너이다.
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 소켓에 무기 부착하기

Skeleton Editor에서 hand_r 본에 WeaponSocket을 생성하세요. 프리뷰 메시로 무기를 설정하여 위치/회전을 조정하고, 블루프린트에서 Attach Actor To Component로 런타임에 무기 액터를 소켓에 장착하세요.

실습 2: Virtual Bone 생성과 활용

Skeleton Tree에서 hand_l과 hand_r 사이에 Virtual Bone을 생성하세요. 이 Virtual Bone이 IK Target이나 Look At 노드의 타겟으로 어떻게 활용되는지 AnimBP에서 테스트하세요.

심화 과제

C++에서 FReferenceSkeleton을 순회하며 전체 본 계층을 트리 형태로 로그 출력하는 함수를 작성하세요. 각 본의 컴포넌트 스페이스 트랜스폼을 계산하고, 소켓 트랜스폼과 본 트랜스폼의 관계를 디버그 드로잉으로 시각화하세요.