fbx
파일을 로드해서 모델을 렌더링하려고 한다. fbx
파일을 로드하려면 FBX SDK
관련 셋팅이 필요하다. 관련 내용들은 FBX SDK Help | Getting Started | Autodesk 에서 참고하였다.
FBX SDK 다운로드 및 설치
FBX | Adaptable File Formats for 3D Animation Software | Autodesk 우선 이곳에서 SDK 다운로드를 한다. 다운로드 이후 설치까지 한다.

설치된 내용이다. 필요한 파일들을 복사해서 사용하기 때문에 복사 이후에는 uninstall을 통해서 설치를 해제해도 무방하다.
프로젝트에 필요한 파일들 복사
프로젝트에 필요한 내용은 다음과 같다.
include/*.*
lib/x64/debug/libfbxsdk.dll
lib/x64/debug/libfbxsdk.lib
lib/x64/debug/libfbxsdk-md.lib
lib/x64/debug/libfbxsdk-mt.lib
lib/x64/release/libfbxsdk.dll
lib/x64/release/libfbxsdk.lib
lib/x64/release/libfbxsdk-md.lib
lib/x64/release/libfbxsdk-mt.lib
include
는 전체 복사가 필요하고, lib
는 필요한 파일들만 복사하면 된다.

위와 같이 파일들을 프로젝트 폴더에 복사해주었다. 이 파일들을 github
에 올리기 위해 Git LFS를 사용했다. 2.0gb
용량 제한이 있는데 벌써 800메가를 사용해버렸다. fbx
파일을 독자 포맷으로 컨버팅해서 사용해야할 듯 하고, 컨버터는 별도 프로젝트로 분리하는것을 심각하게 고민해봐야겠다.
프로젝트에서 사용전 설정
추가 포함 경로 추가

위와 같이 fbx sdk
의 include
경로를 추가해주었다. sdk
안에서의 헤더파일들의 경로 참조 때문에 꼭 해주어야 한다.
lib 포함하기
#include <fbxsdk.h>
#ifdef _DEBUG
#pragma comment ( lib, "Fbx/debug/libfbxsdk.lib" )
#else
#pragma comment ( lib, "Fbx/release/libfbxsdk.lib" )
#endif
debug
, release
모드를 구분해서 lib
파일을 포함하도록 추가해주었다.
dll 파일 복사 이벤트 추가

xcopy /Y /I "$(SolutionDir)\Engine\Extras\Fbx\debug\libfbxsdk.dll" "$(TargetDir)"
를 빌드전 이벤트로 추가해주었다. 런타임 라이브러리 포함이 필요하다. 실행 파일 경로로 복사해주며 debug
, release
모드를 구분해서 원본 파일의 위치가 다르도록 따로 이벤트를 추가해주었다.
여기까지 하면 fbx
파일을 로드하는 준비는 모두 마쳤다.
FBX 파일 로드하기
fbx
파일을 로드하기 위해 필요한 인스턴스는 다음과 같다.
FbxManager* FbxManagerPtr = nullptr;
FbxIOSettings* FbxIOSettingsPtr = nullptr;
FbxImporter* FbxImporterPtr = nullptr;
FbxScene* FbxScenePtr = nullptr;
이 인스턴스들을 생성하고 초기화 하는 과정이 필요하다.
FbxManagerPtr = FbxManager::Create();
FbxIOSettingsPtr = FbxIOSettings::Create( FbxManagerPtr, IOSROOT );
FbxImporterPtr = FbxImporter::Create( FbxManagerPtr, "" );
FbxScenePtr = FbxScene::Create( FbxManagerPtr, "" );
if ( !FbxManagerPtr || !FbxIOSettingsPtr || !FbxImporterPtr || !FbxScenePtr ) return;
FbxManagerPtr->SetIOSettings( FbxIOSettingsPtr );
FbxImporterPtr->Initialize( FbxFilePath, -1, FbxIOSettingsPtr );
FbxImporterPtr->Import( FbxScenePtr );
FbxImporterPtr->Destroy();
여기까지 하면 일단 fbx
파일 로드까지 완료되었다. 이제부터 fbx
파일을 내가 사용하기 위한 데이터로 컨버팅 하는 과정이 필요하다.
DirectX 기준 좌표계와 삼각형의 형태로 변환하기
fbx
는 맥스나 마야등 저장한 프로그램에 따른 좌표계 기준으로 메쉬 데이터가 저장된다. 또한 삼각형이 아니라 사각형 등 Primitive
또한 다양한 형태로 저장되어 있을 수 있다. 따라서 사용하는 목적에 맞게 컨버팅해서 데이터를 가지고 와야 한다.
좌표계 컨버팅
FbxImporterPtr->Import( FbxScenePtr );
FbxAxisSystem directXAxisSystem( FbxAxisSystem::eDirectX );
directXAxisSystem.ConvertScene( FbxScenePtr );
위 코드처럼 Import
이후 directX
기준의 FbxAxisSystem
을 가져와서 FbxScene
을 변경해준다. 이 컨버팅이 끝나면 내부 데이터의 좌표값들을 directX
기준으로 갖게 된다.
컨버팅 오류
좌표계 컨버팅을 했을때, 렌더링 되는 모델링의 모습이 조금 올바르지 않다. 이건 FBX SDK의 샘플 프로젝트를 사용해서 보아도 마찬가지다. 리소스의 이슈일 수도 있다. 일단은 이 부분은 컨버팅 하지 않기로 한다. 나중에 다시 살펴볼 문제이다.
삼각형 형태로 변환하기
FbxGeometryConverter fbxGeometryConverter( FbxManagerPtr );
fbxGeometryConverter.Triangulate( FbxScenePtr, true );
위 코드는 FbxScene
에서 Primitive
의 기본 모양을 삼각형으로 변경해준다.
위 컨버팅은 굉장히 오랜 시간이 걸린다. 특히 메쉬가 복잡할수록 더 많은 시간이 걸리게 된다. 그렇기 때문에 매번 파일을 로드해서 컨버팅해서 사용할 수 없다. 그래서 컨버팅까지 끝난 데이터는 따로 독자적인 포맷으로 저장 & 로드해서 사용해야 한다. 이것은 나중에 다시 다루도록 한다.
FBX Node 로드하기
fbx
에서의 node는 트리 구조로 구성되어 있다. 그리고 각 노드는 transform
정보를 가지고 있다.

트리를 순회하며 Mesh데이터를 가지는 FBX Node 찾기
void _LoadNode( FbxNode* Node )
{
if ( !Node ) return;
for ( int i = 0; i < Node->GetChildCount(); ++i )
{
_LoadNode( Node->GetChild( i ) );
}
FbxNodeAttribute* nodeAttribute = Node->GetNodeAttribute();
if ( !nodeAttribute ) return;
switch ( nodeAttribute->GetAttributeType() )
{
case FbxNodeAttribute::eMesh: _LoadMeshNode( Node ); break;
default: break;
}
}
위와 같이 재귀 탐색을 하며 Node
의 Type
에 따라 Node
데이터의 임포트를 처리할 수 있다. 모든 타입의 노드를 처리하는 것은 너무 방대하므로 일단 모델 데이터만 가져올 수 있도록 FbxMesh
타입의 Node
만 처리하도록 한다. 나머지는 필요할 때 차차 하나씩 다룰 수 있도록 할 것이다.
FBX Node의 글로벌 변환 매트릭스 가져오기
const FbxAMatrix& transformMatrix = Node->EvaluateGlobalTransform();
위 함수를 통해 해당 노드의 글로벌 변환 매트릭스를 가져올 수 있다.
FBX Node의 Vertex 정보 가져오기 ( Position )
FbxVector4* fbxVertices = mesh->GetControlPoints();
for ( int polygonIndex = 0; polygonIndex < mesh->GetPolygonCount(); ++polygonIndex )
{
for ( int t = 0; t < 3; ++t )
{
int index = mesh->GetPolygonVertex( polygonIndex, t );
const FbxVector4& fbxPosition = transformMatrix.MultT( fbxVertices[ index ] );
CRVector position;
position.x = fbxPosition.mData[ 0 ];
position.y = fbxPosition.mData[ 1 ];
position.z = fbxPosition.mData[ 2 ];
++vertexIndex;
}
}
위와같이 Primitive
를 이루는 Vertex Position
을 가져올 수 있다. 데이터에 미리 전역 변환을 적용해두었다. 물론 이 변환 매트릭스를 따로 저장해두었다가 이 모델을 렌더링할 때 변환 매트릭스를 통해 변환을 해주어도 된다. 실제 샘플 프로젝트는 그렇게 렌더링하고 있다. 하지만 나는 이 데이터를 내 포맷으로 전환 후 사용하고, 일단은 여러 노드로 나누어진 메쉬 데이터를 하나의 Primitve
로 통합해서 임포트할 예정이므로 위와같이 Vertex
의 Position
데이터를 임포트할 때부터 전역 변환을 미리 해두는 것이다.
for ( int polygonIndex = 0; polygonIndex < mesh->GetPolygonCount(); ++polygonIndex )
{
for ( int t = 0; t < 3; ++t )
{
FbxVector4 fbxNormal;
mesh->GetPolygonVertexNormal( polygonIndex, t, fbxNormal );
CRVector normal;
normal.x = fbxNormal.mData[ 0 ];
normal.y = fbxNormal.mData[ 1 ];
normal.z = fbxNormal.mData[ 2 ];
}
}
각 Vertex
의 Normal
은 위와 같이 얻어올 수 있다. 이 외 머티리얼 데이터나 애니메이션 데이터등 임포트해야할 데이터가 아직 많이 남아있으나 이건 나중에 필요할 때 천천히 하나씩 하기로 하고 일단은 Position
과 Normal
만 우선 임포트하도록 한다.
무료 FBX Asset
파일을 다운로드 받아 임포트 후 렌더링 해본 결과 아래와 같은 결과물을 볼 수 있었다.

윈도우 3D 뷰어로 보면 다음과 같은 이미지이다.

뭔가 많이 다른것 같다. 일단 나는 아직 Depth Test
도 하지 않고, Node
의 렌더링 순서도 엉망이라 다르게 보인다. Depth Test
만 해도 정상적인 이미지에 가까운 결과물을 얻을 수 있으리라 생각된다.
yunei0313/CRY at FBX-Load-(-Position,-Normal-)에서 코드 확인이 가능하다.
이전글 : [DirectX11] 15. 카메라( View & Projection ) – Constant Buffer 활용하기 2편.
다음글 : [DirectX11] 17. Depth-Stencil Buffer 사용하기.