친구랑 Unity만지다가....모집공고 올라와서 이력서 넣으려는데 뭔가 텅텅 빈 느낌이라. 급하게 시작한 FBX공부 ㅋㅋㅋㅋ
FBX SDK | Autodesk Platform Services
1. 냅다 content / attribute / Type 정보를 출력해보는 예제 따라하기
- 엄청나게 많은 skeleton과 2개의 Mesh가 있다...흠
- 주어진 코드를 그대로 따라했고 다른 점은, WinApp이여서
- printf 대신에 C++20의 std::format으로 출력창에 출력을 해줬다.
- 모델은 Mixamo 의 이 친구를 사용했다.
2. FBXLayer가 뭐지??
- 일단 Mesh를 얻고 싶어서 옛날 문서를 찾아보다가...
- (안전하지 않은 옛날 링크) FBX SDK Documentation: C++: FbxLayer Class Reference (autodesk.com)
- 볼게 이것 말고 없다...FbxLayer라는 것을 발견했다.
- 노멀, 탄젠트, 바이 탄젠트, Material 등 이런저런 정보를 가지고 있는 클래스 같다...
(그래서 Vertex 랑 Index 는 도대체 어디있는거지?)
- normal / uv / material이 있다고 한다.
- 안에 뭐가 들었는지 출력을 시도해 보겠다.
- 이렇게 파일에 출력하도록 바꾸면?
- 총 2개의 Mesh가 존재하는데 각각 대충 결과는 이렇게 나온다.
(.... 그래서 이걸 어떻게 써먹으라는 거지?)
(게다가 lock 문제로 manager를 destroy 할 때 Exception이 자꾸 생긴다...)
(이거에 대한 해결책은 GetDirectArray() 나 GetIndexArray()을 값으로 받지 말고 포인터로 받으면 된다.)
3. FbxNode로 다시 돌아가기
- FBX SDK Help | FBX nodes | Autodesk 여기 보면 루트노드에서 뭔가 막 하는 것 같은데... 문서를 봐보자.
- FBX SDK Documentation: C++: FbxNode Class Reference (autodesk.com) (설명이 많다... 해석해 볼 가치가 있을 듯?)
- FBX는 모델 말고도, 애니메이션, 카메라, 조명 등 뭔가 많이 들어있는 것 같다. 이 전체를 Scene이라고 부르고, 그 메쉬노드? 애니메이션 노드? 같이 Node들이 모여서 Scene 을 구성하는 느낌인 듯하다.
- 각각의 node 들은 (Scene 안에서) Transform을 가지고 있고, pivots이나 ik stiffness나 daming 값도 가지고 있다.
- Node들의 종류? 속성값을 정의하는 FbxNodeAttribute가 있고... 여기에 맞는 동작들이 있고, 프로그래머가 알맞게 사용하면 되는 느낌인 듯하다.
- 근데... FbxNode의 멤버 함수를 보니까 그냥 이것저것 다 있다.
> GetChild 나 GetParent부터
> Local로 만질 수 있는 Transform이나 좌표계 값
> Node Attribute와 그것으로 특정되는 FbxNode의 자식 클래스로 반환하는 함수
> 쉐이더에서 사용하기 좋은 Matrix 값도 구해서 주는 함수도 있고
> Material도 그냥 가져올 수 있고
> pivot... 이 말하는 게 기존 Mesh Offset을 말하는 건가??? 아니면 bone 최종 위치를 계산하는 전체 과정을 말하는건가???
아무튼 시도해 보자.
4. 일단 FbxMesh 클래스부터 살펴보자.
- 얘는 FbxNodeAttribute -> FbxLayerContainer -> FbxGeometryBase -> FbxGeometry으로부터 상속받은 친구다.
(그 위에 있는 FbxEmitter나 FbxObject는... 매우 일반적인 기능을 제공하는 클래스여서 나에겐 너무 어렵고, 많다.)
- FbxNodeAttribute 는 node 속성을 가질 수 있게 하는 클래스이다. 여기서 (3번 항목에서 언급했던) 정해진 노드 타입(eType) 값으로 얘가 어떤 역할을 할지 정해지는 느낌인 듯하다.
- FbxLayerContainer 는 Layer를 가지고 있는 친구다... (2번 항목에서 했던걸 말하는 건가??).
- 이 클래스에서 멤버로 가지고 있는 FbxLayerElement 가 있는데 위 오른쪽 사진 처럼... 엄청 많은 eType과 Template을 제공한다. (이거랑 엮어서 lock이 가능한 Array도 제공한다.)
- 문서에 보면 위와 같은 타입의 layer 요소들이 geometry surface에 어떤 식으로 mapping이 될 것인가를 정의하고, 그 정보들이 메모리에서(?) 어떻게 올라갈 것인지도 정의한다고 한다.
- 그를 위해서 이 친구가 Normals 이랑 UVs를 소유하고 있다고 한다. (무친...)
- 뭔가... FbxLayerElement, 이 친구가 핵심 역할을 하는 느낌이다.
- FbxGeometryBase를 보자. 클래스 이름도 그렇고, 문서의 맴버 함수들 이름도 그렇고.. Layer에서 Geometry의 기능에 집중한 친구란 걸 알 수 있다. ControlPoint(?) / Normal / Tangent / Binormals 등 뭐가 많다...
- 근데 ControlPoint는 뭐냐... Vertex랑 Index는 어디 감?... 여기 안에 있나? (ㄹㅇㅋㅋ)
- FbxGeometry는 컨트롤 포인트 변형 (control point deformation)을 지원하는 친구다. 즉, 애니메이션을 할 수 있게 하는 친구라는 뜻. (하위 클래스를 보면 Mesh 말고 Nurbs나 Patch가 있다.)
- 멤버나 혹은 연결 관계로 FbxDeformer, FbxGeometryWeightedMap, FbxShape 등등을 가지고 있는 듯하다.
- 그리고 변형을 시켜주는 translation, rotation, scaling에 관한 정보를, FbxAMatrix라는 행렬 타입을 이용해서 geometry위의 모든 control point에 적용(pivot)시키는 함수도 가지고 있다.
- FbxMesh는 Polygon이라는 구조를 이용해서 Geometry를 정의하는 클래스인 듯하다. 멤버도 polygon에서 vertex / normal / uv를 뽑아내는 기능으로 보인다.
- 그리고 FbxLayer 부분에서 하는 Material , Texture, UV 작업도 경유해서 할 수 있도록 기능을 제공한다.
5. 그러면... FbxMesh와 상위 클래스가 가지고 있는 속성을 최대한 출력해 보자.
- 앞에서 뭔지 살펴본 친구들만 테스트용으로 출력해 보는 코드다.
- 너무 길어서 없앰 ㄹㅇㅋㅋ
- 무튼 이것의 결과는 첫 번째 메쉬는 다음과 같고..
- 두 번째 메쉬는 다음과 같다.
(그래서 Vertex랑 Index는 어디 있는데.... ㅡ.ㅡ)
(Material 도 어떻게 쓰는지 모르겠고... ㅡ.ㅡ)
(보니깐 deformer 랑 crease가 하나씩(오류입니다. 없습니다.) 있던데... 이건 또 뭐야 ㅡ.ㅡ)
6. 그냥 샘플코드 보면서 공부해 보자.
- 오... 위와 같은 과정 덕분인지 샘플코드가 읽히기 시작했다 ㄹㅇㅋㅋ (ViewScene, ImportScene)
- 이거를 적절히 변형해 가면서 + 모르는 게 나왔을 때 정리하는 식으로 해야겠다.
7. EMappingMode
- 모르는게 바로 나왔다... eMappingMode
- (이상하게 FbxGeometryElement으로 typedef 되어 있는) FbxLayerElement에 정의되어 있는 enum이다.
- 위에서 언급했던, 엄청나게 다양했던 FbxLayerElement (정확히는 FbxLayerElementTemplate) 들이 Geometry에서 어떻게 매핑이 될지 정해지는... 뭐 그런 거 같다.
- 해석을 대충 해보면.
- eNone : 매핑이 정해지지 않음
- eByControlPoint : 각각의 컨트롤 포인트에 하나씩 매핑 좌표가 존재함
- eByPolygonVertex: 각각의 Vertex와 그것이 포함된 폴리곤마다 매핑 좌표가 존재함 점으로 정해짐. 이것은 한 vertex와 그 vertex가 포함된 polygon과 같은 매핑 좌표를 가질 것이라는 것을 의미함
- eByPolygon : 폴리곤 전체에 하나의 매핑 좌표를 가짐
- eByEdge : 메쉬에 각 유일한 선분마다 매핑 좌표를 가짐. smoothing layer elements과 함께 쓰임을 말함
- eAllSame : 전체 surface에 하나의 매핑 좌표를 가짐
- 쉽지 않다. 일단 내가 가져온 모델은 무슨 친구인지 확인해 보자.
- 내가 가져온 fbx 모델은 두 메쉬 모두 eAllSame 방법으로 mapping이 되어 있다.
- 흠... 아직 잘 모르겠다.
8. EReferenceMode
- EMappingMode와 같이 See also에 붙어있는 친구이다.
- 첨 보는 거라서 들어가서 봤더니...
- "좌표 배열에 매핑 정보가 어떤 식으로 저장이 될지 정한다."
- eDirect : n 번째 매핑 정보가, 직접배열(FbxLayerElementTemplate::mDirectArray)에 n 번째 요소에 존재한다.
- eIndex : (요거는... 버전 업이 되어서 v6.0 이후 버전이면 쓰이지 않는다. eIndexToDirect로 대체되었다.)
- eIndexToDirect : 주소배열(FbxLayerElementTemplate::mIndexArray)은 , n 번째 요소에 대해, 직접배열(mDirectArray)에서의 인덱스를 가지고 있다. eIndexToDirect는 보통 eByPolygonVertex 매핑 모드에 쓰인다. 같은 좌표가 반복적으로 나온다면, 좌표는 한 번만 저장하고 + 그것을 참조하는 index를 여러 번 쓰며 용량을 아낄 수 있다. Material이나 Texture도 같은 모드를 사용하고, (mIndexArray에서 얻은 index를 통해) mDirectArray에서 실제 값을 가져와서 사용한다.
- 각 잡고 해석을 하니까 이해가 되었다. (버텍스 버퍼, 인덱스 버퍼 느낌쓰)
- 그동안 생각 없이 사용하던 GetDirectArray가 위와 같은 의미를 가지고 있었다.
9. eMappingType에 따른 Mesh import 방법
(부제 : 예제를 살펴보면서 끼워 맞추기)
9_1. 머테리얼 Mapping Type에 따른 사전작업(?)
- Mesh를 분해하기 전에 MaterialElement의 Mapping Type을 얻어서, eByPolygon의 경우 추가 작업을 한다.
eByPolygon의 경우]
1) 일단은, 멤버로 (int)IndexOffset과 (int)TriangleCount를 가지는 구조체로, 배열을 만든다.
2) 전체 polygon 개수를 제어 변수 인덱스로 이용해서, MaterialElement의 IndexArray에서 값을 가져온다. 가져온 값은 MaterialElement의 DirectArray의 인덱스값이 될 것이고... 이 인덱스에 해당하는 SubMesh에 TriangleCount을 1로 해준다.
- polygon Index로 Material IndexArray를 참조하여 나온 값이 SubMesh 배열을 참조하는 Index로 쓰인다.
- 매 루프마다 가장 큰 값만큼 Submesh 배열의 크기를 지정해 준다.
3) Material 개수만큼 생긴 Submesh 배열을 돌면서 삼각형 점 개수 * 3 만큼 오프셋을 늘려주면서 값을 기록해 준다.
- Material IndexArray의 값이 없는 SubMesh는 TriangleCount 가 0이었으니, offset 값은 이전과 똑같은 값을 가질 것이다.
(근데 애초에... Material이 개수가 그렇게나 많나?)
그 이외의 경우]
- SubMesh 구조체를 사용하지 않는다.
9_2. Index / Normal / UV 구하기 (+ tangent, bitangent)
- Mesh가 Normal Element나 UV Element를 가지고 있는지 확인한다. 그리고 Normal과 UV 엘레먼트의 Mapping Type을 확인하는데, eByControlPoint의 경우와 그렇지 않은 경우가 나뉜다.
(예제를 보면 eByControlPoint의 경우는 Control Point에 다 붙어있는 느낌이고 / 그 이외는 Polygon 단위 느낌이다.)
eByControlPoint의 경우]
- Mesh에서 GetElementNormal / GetElementUV으로 FbxLayerElementTemplate 배열을 얻어서 접근한다.
- 배열 제어는 Vertex(Control Point) 개수로 한다. (여기도 배열의 RefMode에 따라 IndexArray를 한번 거쳐야 할 수 있다.)
- Vertex는 control Point 자체가 Vertex가 된다. 제어변수값 그대로 참조하면 된다.
- Index는 폴리곤 루프를 돌면서, GetPolygonVertex로 구한다.
그 이외 경우]
- 폴리곤 단위로 값을 가져오는 느낌이다. Normal 값과 UV 값을 GetPolygonVertexNormal()과 GetPolygonVertexUV()으로 구한다. (폴리곤 + 버텍스 2중 루프이다.)
- Vertex는 GetPolygonVertex가 폴리곤 루프를 돌면서 나온 값으로 , GetControlPoints()으로 얻은 배열에 참조하여 구한다.
- 인덱스는 폴리곤 루프 제어변수 값 그대로 넣으면 된다.
9_3. 머테리얼 매핑타입에 따른 Index 저장 위치
- 위에서 했던 사전 작업(SubMesh)을 이용하는데... 왜 이런지는 잘 모르겠다.
eByPolygon의 경우]
- 머테리얼 개수만큼 SubMesh를 만들었고, 멤버 indexOffset 값에 3씩 늘려가며 값을 채워주었다.
- 제어 변수인 polygonIndex로 MaterialElement의 IndexArray를 참조해서 나온 값으로 SubMesh 배열을 참조한다.
- 참조한 SubMesh의 IndexOffset 값을 polygonIndex 대신 사용한다..
(왜 이런 거지?)
그 이외의 경우]
- 차례대로 넣어주면 된다.
10. FbxSurfaceMaterial
- 9번의 이유를 알기 위해서 이걸 공부해야 하는 것 같은데...
- 다음 글로 넘겨야겠다... 너무 길어진다.
11. 일단 Mesh만 그려본 결과
- 삼각형이 빈다... 왜 이러지?
12. FbxGeometryConverter - Triangulate
- 여러모로 아쉬운 Mixamo 모델을 Triangulate를 해주면?
(Triangulate 작업이 엄청 오래 걸린다)
- 그래도 너무 까맣다. 노멀 방향이 안쪽으로 있나?
- 일단 방법을 모르겠으니 Normal이 저장되는 방향을 냅다 거꾸로 해주자.
(근데.... 나는 Material Albedo에 회색을 넣었는데, SkyCube 반사에서 문제가 생겼나?)
- 민짜 노멀맵과 민짜 범프맵이여서 문제가 안생길 줄 알았는데...
- fbx 모델에 없던 Tangent를 임의로 (대충 World Up과 모델 중앙으로 근사) 계산해서 넣어줬는데 그게 문제가 생겼던것 같다... 쉐이더 코드를 범프맵을 사용하지 않도록 고쳤더니 잘 작동한다.
13. 코드 좀 정리하고 다음 글에 쓰기
- 삽질한 것까지 전부 적다 보니까 글이 너무 길어졌다.
- 다음글에 Material을 해보겠다.
- 이력서 넣기 전에 animation 찍먹 해봐야 하는데... 큰일 났다.
'Programming > D3D12' 카테고리의 다른 글
(공부중) #1 App 구조 예쁘게 다시 바꿔보기 (0) | 2024.07.01 |
---|---|
[일기장] 무작정 FBX SDK 사용해보기 (Material + Animation) (0) | 2024.05.05 |
[책공부] 캐릭터 애니메이션 chap 23 (연습 문제 X) (0) | 2024.03.25 |
[책공부] Quaternion + chap 22 연습 문제 (0) | 2024.03.12 |
[책공부] Ambient Occlusion + chap 21 연습 문제 (0) | 2024.03.08 |