[DX11] FBX Import

윤정민·2026년 1월 25일

Graphics

목록 보기
24/24

목표

StaticMesh를 렌더할 수 있도록 엔진을 만들었다.
해당 엔진에 FBX 파일을 임포트하여, StaticMesh로 변환한 뒤 렌더하는 기능을 구현한다.

아이디어

  • IMGUI를 통해 모델 파일경로 입력
  • Assimp을 통해 FBX 파일 읽기
  • 읽어온 ai 데이터를 엔진 데이터로 변환
    • MeshAsset
    • MaterialInstance

서브 메시 개념은 생각하지 못해 이후에 도입했습니다.

구현 내용

1. Assimp 임포트

1.1. 임포트 플래그

Assimp에서 제공하는 플래그를 통해 내가 만든 엔진 구조에 맞게 읽음

  • aiProcess_Triangulate
    • 모든 면을 삼각형으로 변환
  • aiProcess_JoinIdenticalVertices
    • 동일 버텍스 병합
  • aiProcess_ImproveCacheLocality
    • GPU 캐시 효율 향상을 위한 인덱스 재정렬
  • aiProcess_SortByPType
    • Primitive 타입별 정리
  • aiProcess_FlipUVs
  • UV 반전
  • aiProcess_ConvertToLeftHanded
    • 좌수계 변환

1.2. Assimp 데이터 구조

aiScene
Assimp::Importer::ReadFile()의 결과는 aiScene*이며, 이 객체가 임포트된 모델의 루트 컨테이너다.

주요 멤버

  • scene->mRootNode : 노드 트리의 루트
  • scene->mMeshes[] : 메시 배열(실제 geometry 데이터)
  • scene->mMaterials[] : 머티리얼 배열
  • scene->mNumTextures / mTextures[] : embedded texture(내장 텍스처) 배열

이걸 봤을 때 Mesh와 Material이 배열인걸 보고 뭔가 잘못됐다 생각함. 내 엔진은 StaticMeshComp 1--1 MeshData 1--1 Material 1대1구조임

aiNode
Assimp 구조에서 노드(aiNode)는 트랜스폼을 가진다.
반면, 메시 데이터는 scene->mMeshes[idx]에 있다.

  • 노드는 node->mMeshes[]참조하는 mesh index를 보유
  • 실제 버텍스/인덱스는 scene->mMeshes[meshIndex]에 존재
  • 따라서 FBX 같은 포맷은 보통 node 트리를 순회하면서 누적 트랜스폼을 적용해야 제대로 보인다.

최종 코드에서는 DFS로 노드를 순회하며 누적 트랜스폼을 만든다:

  • global = parent * node->mTransformation
  • 각 node가 가진 mesh index에 대해 scene->mMeshes[meshIndex]를 처리

2. Mesh 빌드

2.1. 버텍스 생성

  • Position: p' = global * p
  • Normal: inverse-transpose(upper3x3(global)) 적용 후 normalize

이를 통해 노드 트랜스폼(특히 스케일/회전)이 포함된 올바른 월드 공간 지오메트리를 얻는다.

2.2. 머지 시 고려할 점

기존 엔진 구조로 임포트 하기 위해 하나의 aiMesh만 사용해 임포트 하였더니 특정 부위가 정상적으로 구성되지 않은 것을 확인했다.

aiNode가 N개 이고, 다른 meshIndex를 사용하는 경우였다. 이를 고려해 ChildNode를 순회하며 버텍스를 생성해줘야한다.

ChildNode의 버텍스 정보는 캐싱해둬야 한다. 자세한 내용은 Material에서 다룬다.

3. Material 빌드

3.1. Texture 로드

텍스쳐 경로에 따라 처리 로직을 나눈다

텍스쳐가 FBX에 포함되어 있는 경우

  • if (const aiTexture* at = scene->GetEmbeddedTexture(t8.c_str()))를 통해 가져오기 시도
  • CreateSRVFromEmbeddedRawBGRA8를 통해 SRV 변환

scene->mTextures를 레퍼런스 하는 경우

  • texture path string이 "*index" 형태인지 검사
  • scene->mTextures접근하여 CreateSRVFromEmbeddedRawBGRA8를 통해 SRV 변환

외부 경로인지 확인

  • 경로에 파일이 있는지 확인하여 로드
  • DirectX::CreateWICTextureFromFile

3.2. 로드시 고려할 점

위에서 봤듯이 scene->mMaterials[]은 배열이다.
각각의 메시가 참조하는 머티리얼이 다르다.
따라서 엔진에서도 메시를 작은 단위 (굳이 노드가 아니여도 됨)로 나누고 작은 단위의 메시 마다 머티리얼 참조를 할 수 있는 구조를 만들어야한다.

나는 하나의 머티리얼만 사용하는 줄 알고 0번을 레퍼런스 하도록 구현했었는데 아래와 같이 0번 머티리얼을 사용하지 않는 메시의 머티리얼이 비정상적인 것을 볼 수 있다

이를 해결하기 위해 아래와 같이 대응했다

  • 메시를 Section으로 나눌 수 있도록 데이터 구조 변경
  • Section 마다 MaterialIndex를 가지고 있도록 변경
  • MaterialInstance를 Array로 가지고 있도록 변경
  • Section마다 나눠 DrawCall

결과

1. 메시 NormalVector 임포트 잘 되었는지 확인

2. 메시 데이터 및 머티리얼 임포트 확인

  • 오른쪽
    • 최종 결과
  • 왼쪽
    • 제공된 FBX 파일
  • 중간
    • 임포트 실패 과정...

profile
그냥 하자

0개의 댓글