Assimp를 사용해 fbx파일 로드하기
지금까지는 기본도형 큐브같은걸 코드로 만들어서 갖다 썼지만 유니티짱같은건 만드는게 말이 안된다.
그래서 맥스나 마야로 만든 파일을 가져다가 쓸 것이다.
그 맥스나 마야에서 추출하면 fbx타입으로 나오고 이를 로드할수 있도록 도와주는게 Assimp라이브러리다.
유니티에서 fbx파일을 열어서 보면 하나의 파일이 여러 정보를 포함하고있는데 대부분 메쉬이고 경우에 따라서 머티리얼이나 조명도 포함하고있다.
드래곤 모델 같은경우는 라이트와 카메라도 들고있다.그런데 왜 이렇게 많은 정보를 포함하고 있을까? 지오메트리 정보(정점정보)만 갖고있으면 되는게 아닐까 싶지만 꼭 모델이 게임에만 쓰이라는 법은 없다. 애니메이션, 영화용으로 쓰일 수도 있기 때문에 복잡하다.
그래서 fbx로 만들어진 모델 로드하고 필요한 정보만 골라서 써야하는데 경우에 따라서 한번만 배치하고 끝나는 물체가 있고 애니메이션에 결합해서 써야하는 물체가 있다. 언리얼에선 이를 스태틱메쉬와 스켈레탈 메쉬로 구분해서 쓰기도 한다.
만약에 드래곤이 고개를 좌우로 젓는 애니메이션을 추가한다고 하면 2D라면 이미지 여러장을 붙여서 애니메이션을 만들겠지만 3D에선 고개를 돌린 메쉬를 또 추출해서 프레임마다 메쉬를 바꾼다는것은 솔직히 에바무리다. 메쉬를 InputAssembler단계에 꽂아서 GPU에 줘야하는데 정점 수가 바뀌고 연산량도 엄청나게 된다. 아마도 ConstantBuffer나 다른 무언가를 사용할 방법이 있을 듯하다. 애니메이션은 다음에 다룰것이나 미리 생각해보면 유니티에서도 그랬고 언리얼에서도 그렇고 오브젝트는 계층구조를 이루고 있었다.
척추가 있고 팔, 다리, 몸, 머리 그리고 각각도 세분화되어 계층 구조를 만들어 준 다음에 어느 부분만 움직이게끔 수학적으로 계산해서 움직이는게 3D 모델의 게임에서 동작 원리이다.
위 유니티짱의 Hierachy에서 볼 수 있듯이 하나의 fbx파일임에도 위와 같은 계층구조로 들어가 있는 경우가 많기 때문에 파싱할때부터 이 계층구조를 생각해서 작업해야한다.
그리고 이 계층구조는 트리를 이용하는게 직관적이다. 보통 트리는 읽어드릴때 순서가 보장이 안되므로 재귀적으로 루트를 기준으로 실행하는 함수를 만들어 자식에도 적용시키는 방법으로 만들 예정이다. 미리 이 계층구조를 fbx파일로부터 읽어서 원하는 포맷으로 저장할 것을 생각하면, 한 fbx파일에는 계층구조를 이용해 각각 다른 자식 메쉬들이 Tranform정보같은 것을 들고 있다. 그리고 각각의 Transform정보는 자신의 부모를 기준으로 하는 자신의 좌표이다. 이 각각의 좌표를 로컬의 좌표라고 하면 로컬스페이스와 혼동이 있을 수 있으니 '상대좌표'..? 라고 루키스는 부르기로한다. (월드 기준이 아니라 직접 상관을 기준으로 하는 상대적인 좌표다는 의미에서)이들의 정보를 들고 있을 것인데 이 상대좌표를 Transform의 정보에서 SRT행렬을 얻어서 곱하면 world가 아닌 직속상관(부모)로 넘어가게 된다.
그리고 이 상대좌표를 가진 계층구조안의 어떤 메쉬가 월드좌표를 알고자 한다면 부모의 SRT를 곱하고 또 계속해서 그 부모의 SRT를 곱하고 월드까지 올라가면 된다.
그래서 쉐이더 코드에서 이 상대좌표 정보를 처음에 fbx로 읽어 가지고 쉐이더 코드에서 연산할 때는 월드에서의 좌표를 결국 필요로 하는데, 매번 상대좌표를 월드좌표까지 계속 SRT행렬 곱연산을 할게 아니라 처음부터 이 상대좌표를 우선 오브젝트의 root라고 할수 있는 로컬좌표계의 좌표로, fbx파일을 처음부터 읽어드릴때 변환해주고 시작해야한다.
=> 계층구조를 가진 오브젝트의 메쉬에서 각각은 직속 부모를 기준으로한 상대좌표를 가지고있는데 이를 오브젝트의 로컬좌표계 기준으로 바꿔서 들고 있게 한 다음에 쉐이더에 넘어가면 월드, 뷰, 프로젝션 좌표계로 변하게 되는 것이다.
그리고 로컬좌표계의 좌표로 변환된 상대좌표를 쉐이더에 넘어가면 일반 오브젝트와 똑같이 World, View 변환 행렬에 곱해져 Projection행렬까지 WVP를 파이프라인에서 타게 될 것이다.
또 반면 애니메이션을 틀 때는 계층구조 안에 있는 메쉬의 상대좌표가 얼마만큼 바뀌느냐로 나타난다.(이 아니라 상대좌표 기준으로 로컬 좌표가 얼마나 움직이는지를 나타내나?)
이들을 실제로 저장할때는 로컬 좌표계로 변화하는 행렬로 할 것이다.
그래도 이 계층구조에 관한 것은 트랜스폼을 다룰때도 봤었다.
fbx파일을 로드하는 라이브러리는 여러가지가 있는데 Assimp라이브러리는 fbx외에도 obj파일도 로드할 수 있는 좀더 범용적인 라이브러리다.
assimp로 로드한 파일을 바로 쓰기도 하지만 보통 직접 짠 구조에 맞게 포맷을 변경한 다음 메모리에 들고 있게 하기도 한다.(fbx에서 로드한 모든 정보-조명 등-가 필요한게 아니라서) 그 포맷으로 최초로 assimp로 로드한 파일을 바꿔서 저장한다음 다시 로드할때 사용자 지정 포맷으로 하면 속도에서 더 우수하기도 하다.
https://github.com/assimp/assimp
Assimp라이브러리는 위 깃헙에서 가져오 CMake로 윈도우용으로 빌드한다.( 리눅스에서는 바로 사용할 수 있지만 윈도우에서는 힘들기 때문에 Cmake로 빌드해서 만드는 방법 )
Libraries의 Lib,Include에 CMake로 빌드해서 나온 Assimp 라이브러리와 헤더파일들을 각각 추가해줬다.
이들을 사용할 Engine 프로젝트에 라이브러리 추가하는 방법
Assimp를 이용해 fbx파일을 로드해 원하는 포맷으로 저장해줄 툴을 만들것인데 Engine,Client프로젝트와도 무관하기때문에 솔루션에 새로운 프로젝트를 추가해준다. 우선 Windows데스크톱 마법사로 AssimpTool이름으로 미리컴파일된 헤더를 포함하여 빈프로젝트로 데스크톱 애플리케이션(.exe)가 출력되도록 만들었다.
AssimpTool프로젝트의 출력디렉토리도 Client프로젝트와 동일하기 '$(SolutionDir)Binaries\'로 바꿔주고 중간디렉토리도 동일하게 '(SolutionDir)Intermediate\(Configuration)\'으로 수정해주었다.
그리고 Client프로젝트의 C/C++>추가포함디렉터리도 복붙했다.(여기에 Include)
마지막으로 링커>입력>추가라이브러리디렉터리도 Client프로젝트걸 복붙했다.(여기에 Lib)
그리고 AssimpTool에 Client프로젝트의 Main.h,cpp, CameraScript.h,cpp파일을 넣었다. Main에서 WinMain함수가 있어서 진입점을 바꾼것이다.
AssimpTool의 pch.h에도 Engine라이브러리를 사용할 수 있도록
#pragma comment(lib, "Engine/Engine.lib")
#include "Engine/EnginePch.h"
를 추가하였다.
실제로 fbx파일을 로드해서 원하는 포맷으로 저장을 위할 파일들을 위해 Utils 필터를 만들고 Converter클래스도 추가했다.
그리고 Converter클래스에는 Assimp라이브러리의 importer객체를 생성해 실제로 fbx 파일을 읽어오도록 쓸 것이다.
또 Client프로젝트와 마찬가지로 Main에서 IExecute를 상속받은 앱이 실행단위로 실행되도록 AssimpTool클래스를 IExecute를 상속받게 만들었다.(IExecute는 Engine프로젝트에 있어서 쓸 수 있다.)
마지막으로 fbx를 최초로 Assimp로 로드한 그대로 사용하지 않고 직접 정의한 구조로 따로 저장했다가 읽어서 사용할 것인데 이때 정의할 구조를 AsTypes파일을 파서 정의하고자 한다.
아래와 같은 필터 구조를 가진다.
그 다음 이제는 우리가 모델들 fbx파일들을 많이 로드할건데 이들을 Resources디렉터리에 넣고있을 것이다. 이때 파일구조를 어떻게 할지 고민인데 모델하나하나마다 디렉토리를 생성해서 가지고 있을 수도 있고, Assets디렉토리를 Resources하위에 만들어서 가지고 있을 수도 있다. 우선 여기선 후자의 방법을 선택하Assets디렉토리에 모델 하나하나씩 넣고 Assimp로 이들을 읽어들여 원하는 포맷으로 변경한 파일을 Resources에 Models디렉토리에 넣어둘 것이다.
Textures에는 이전까지 테스트하는데 쓰던 디퓨즈맵, 노멀맵 등이 들어있었다.
다음시간엔 이 AssimpTool을 사용해서 fbx파일을 로드하고 직접 정의한 포맷으로 파일을 변환해 저장한다음, 다시 읽고 모델을 띄우는것까지 진행해보자.
'[DirectX11]' 카테고리의 다른 글
모델 #3 모델 띄우기 (0) | 2024.09.19 |
---|---|
모델 #2 Assimp라이브러리 이용해 Material, Bone, Mesh로딩하기. (0) | 2024.09.19 |
Normal Mapping (0) | 2024.09.18 |
Material (0) | 2024.09.10 |
Light #5 Light 통합 (0) | 2024.09.09 |