FBX SDK для Directx11 Урок 46

Fbx Sdk Directx11 Tutorial 46



В предыдущей серии статей о том, как начать работу с DX11, есть одна Руководство по Directx11 40 загрузка модели OBJ Блог, который читает данные модели obj. Однако в блоге, который прочитал obj, я не сказал, что были какие-то подводные камни, то есть написанный мной анализатор obj может анализировать только определенные форматы файлов obj, потому что позже я использовал написанный мной анализатор модели obj и обнаружил, что не мог разобрать большую часть. Формат файла obj действительно дает сбой, потому что я был парсером, который анализировал один или два конкретных файла obj. Фактически, разнообразие данных файла obj превзошло мое воображение. Поэтому я не рекомендую всем использовать obj, пожалуйста, держитесь подальше от obj. obj может напрямую просматривать текстовые данные, но его нелегко использовать. Мы идем в ногу с коммерческими движками и используем формат FBX.




Соглашение, выпустите соответствующую структуру этой программы, связанной с блогом:







О Business Engine и FBX SDK

Мы знаем, что импортированные модели UE4 и U3D являются файлами FBX, что означает, что файлы FBX являются промежуточными файлами моделей UE4 и U3D. Почему вы добавляете именно «промежуточное звено»? Это потому, что файл модели FBX - это только импортированный файл модели UE4 и U3D, а не файл модели, загружаемый при запуске игр UE4 и U3D. Такие движки, как UE4 и U3D, импортируют файлы FBX для создания собственных файлов пользовательских моделей. Почему это? После использования FBX SDK для чтения данных модели вы обнаружите, что геометрические данные (например, данные вершин), полученные напрямую с помощью FBX SDK для чтения файла модели FBX, имеют определенную (иногда даже серьезную) избыточность, и считайте скорость недостаточно высока, поэтому файл FBX является просто промежуточным файлом модели для коммерческого движка. Конечно, мы используем в качестве кейса учебник по DX11. Нет нужды усложнять это дело. Вы можете напрямую использовать FBX SDK для чтения данных файла FBX в качестве данных рендеринга.




Конфигурация среды FBX SDK.

Я использую VS2017 и FBX SDK2017.1,

Адрес загрузки FBX SDK2017.1: http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&id=26012646

Соответствующие документы разработки: http://help.autodesk.com/view/FBX/2017/ENU/ , Вы можете следовать документации, чтобы настроить среду

Поскольку используется VS2017, соответствующий FBX SDK2017 предоставляет только скомпилированные версии VS2015 и VS2013. Есть три схемы конфигурации среды. Я попробовал все три и обнаружил, что VS2017 может использовать конфигурацию DLL.

Чтение файла FBX

Что касается чтения данных FBX, я не буду здесь показывать свое уродство, а просто процитирую блоги, написанные предшественниками. Следующие три блога о чтении данных FBX настоятельно рекомендуются:

1. Анализ и загрузка модели FBX на основе FBX SDK- (1)

два. Анализ и загрузка модели FBX на основе FBX SDK- (2)

3. Работа с FBX SDK (2)

Четыре. Разобрать файл модели FBX как модель рендеринга Direct3D

Следуйте этим трем блогам, чтобы понять основную концепцию чтения данных FBX SDK.

Единственное, что здесь стоит запутать, так это то, что интерфейс чтения материала изменился. Поскольку нам нужно прочитать файл в DX11, мы должны получить относительный путь к файлу текстуры (абсолютный путь не рекомендуется). Используемый интерфейс - FBXFileTexture вместо FBXTexture, как показано ниже:

void ImportFBX::ReadReletiveTextureFileName(FbxProperty* mproperty, int materialIndex, map& materialMap) { if (!mproperty || !mproperty->IsValid()) { return } string name = mproperty->GetName() bool isNeedTexture = false isNeedTexture = (name == FbxSurfaceMaterial::sDiffuse) || (name == FbxSurfaceMaterial::sSpecular) || (name == FbxSurfaceMaterial::sTransparentColor) || (name == FbxSurfaceMaterial::sBump) if (!isNeedTexture) { return } int textureNum = mproperty->GetSrcObjectCount() //Now only one of each texture is read if (textureNum > 0) { FbxFileTexture* fbxFileTexture = mproperty->GetSrcObject(0) string relativeFileName = fbxFileTexture->GetRelativeFileName() size_t tgaTagPos = relativeFileName.find('.tga') if (tgaTagPos != string::npos) { relativeFileName = relativeFileName.substr(0, tgaTagPos) relativeFileName += string('.jpg') } string fileName = fbxFileNamePre + relativeFileName if (name == FbxSurfaceMaterial::sDiffuse) { materialMap[materialIndex].diffuseMapFileName = fileName } else if (name == FbxSurfaceMaterial::sSpecularFactor) { materialMap[materialIndex].specularMapFileName = fileName } else if (name == FbxSurfaceMaterial::sTransparentColor) { materialMap[materialIndex].alphaMapFileName = fileName } else if (name == FbxSurfaceMaterial::sBump) { materialMap[materialIndex].bumpMapFileName = fileName } } }

Хорошо, здесь я читаю только diffuseTexture, NormalTexture, SpecularTexture, AlphaTexture, и поскольку DXUT не может загружать текстуры в формате .tga, я преобразовал суффикс файла .tga в .jpg и добавил все изображения .tga соответствующего файла FBX, преобразованного в. jpg в PS.


Прочтите структуру данных FBX:

Мы знаем, что узлы FBX организованы в виде дерева, поэтому лучше организовать их деревьями. Конечно, для простоты урока я просто использую массивы для их организации.

1. Вершинная структура выглядит следующим образом, особо нечего сказать.

struct VertexPCNTT { XMFLOAT3 pos XMFLOAT3 color XMFLOAT3 normal XMFLOAT3 tangent XMFLOAT2 uv }


2. Треугольная структура, потому что в узле fbxNode fbx каждый считанный треугольник имеет идентификатор материала, который индексируется для соответствующего материала, а соответствующий материал содержит различные соответствующие атрибуты (Diffuse, Specular и различные текстуры). Здесь следует отметить, что идентификатор материала привязан к соответствующему узлу. Например, один и тот же идентификатор материала для fbxNode1 и fbxNode2 (например, как 0, так и 1) вообще не имеет отношения, а идентификатор материала предназначен только для привязки узла Set.

struct Triangle { VertexPCNTT vertexs[3] int MaterialId }


3. Структура материала. Здесь наш материал напрямую представлен именем файла текстуры. Если имя файла соответствующей текстуры пусто, это означает, что соответствующая текстура отсутствует, в противном случае соответствующая текстура существует.

struct Material { string diffuseMapFileName string specularMapFileName string alphaMapFileName string bumpMapFileName }

4. Структура сетки, как мы можем узнать из предыдущих четырех блогов, все треугольники, считываемые узлом сетки, могут иметь более одного идентификатора материала, и каждый идентификатор материала привязан к соответствующему fbxNode, потому что тип сетки fbxNode или fbxMesh имеет как много идентификаторов материалов, так как существует столько же сеток. (Здесь невозможно, чтобы треугольник был сеткой. DrawCall пугает, поэтому при чтении данных fbxMesh необходимо назначать треугольники с одним и тем же идентификатором материала. Для одной и той же сетки). Вообще говоря, если в fbxMesh существует множество идентификаторов материалов, то сколько сеток создается, то есть в одной сетке есть один идентификатор материала.

struct Mesh { vector mVertexData vector mIndexData int materialId ID3D11Buffer* mVertexBuffer ID3D11Buffer* mIndexBuffer }

5. Структура модели. Только что мы сказали, что сетка fbxNode или fbxMesh может анализировать несколько сеток, а затем все сетки fbxNode анализируются в модель. Но обратите внимание, что я добавил хеш-таблицу материалов в структуру модели, и сетка может находить соответствующий материал (соответствующий относительный путь текстуры) в структуре модели через свой собственный идентификатор материала.

struct Model { vector mMeshList map mMaterialMap }


6. Структура FBXModel, мы сказали выше, что модель - это структура, анализируемая узлом, и если файл fbx содержит n нескольких узлов fbxNode (fbxMesh), то есть создается n нескольких структур модели, и я анализирую их как FBXModel. Примечание. Я использовал хеш-таблицу для запроса соответствующего ресурса ID3D11ShaderResourceView *. Ключом этого mSRVMap является относительный путь текстуры. Вы можете увидеть это внезапно.

//Ensure that all texture files loaded by an FBX are loaded only once struct FBXModel { vector mModelList map mSRVMap }

Идея здесь очень ясна. При рендеринге мы используем меш как отдельный объект и рендерим один за другим. Процесс мышления:

(1) Установите кеш вершин и кеш индексов меша.

(2) Найдите соответствующий Материал в соответствующей хэш-таблице названий материалов модели в соответствии с MaterialId материала сетки и, наконец, найдите соответствующий ресурс ID3D11ShaderResourceView * в хеш-таблице материалов FBXModel с соответствующими именами путей четырех текстур, соответствующих Материал, а потом использовать соответствующие шейдерные рендеры, ну вдруг все стало понятно.


Коррекция пространства системы координат чтения данных FBX:

Потому что мы моделируем в 3DS MAX, а пространство системы координат нашего 3DS MAX выглядит следующим образом:


Пространство системы координат 3DS MAX - это правая система координат по оси Z, а D3D11 - это левая система координат по оси Y. Следовательно, если вы напрямую визуализируете данные, считанные FBX, вы обнаружите, что модель часто лежит. Я использовал метод рендеринга -90 градусов вокруг оси X и преобразовал данные вершин следующим образом:

1. Расположение (XMMatrixRotationX (-XM_PI / 2.0)),

//Because the coordinate axis in 3DS MAX is the Z-axis upward, the Y-axis is the right-hand coordinate system, and D3D11 is the left-hand coordinate system //Reference https://www.cnblogs.com/wantnon/p/4372764.html void ImportFBX::ReadVertexPos(FbxMesh* mesh, int ctrlPointIndex, XMFLOAT3* pos) { FbxNode* meshNode = mesh->GetNode() FbxAnimEvaluator* lEvaluator = mScene->GetAnimationEvaluator() FbxMatrix lGlobal lGlobal.SetIdentity() lGlobal = lEvaluator->GetNodeGlobalTransform(meshNode) FbxDouble3 scaling = meshNode->LclScaling.Get() FbxVector4 * ctrPoints = mesh->GetControlPoints() pos->x = ctrPoints[ctrlPointIndex][0] * scaling[0] pos->y = ctrPoints[ctrlPointIndex][2] * scaling[2] pos->z = -ctrPoints[ctrlPointIndex][1] * scaling[1] }

2.Vertex normal (обратное матричное преобразование преобразования положения вершины)

void ImportFBX::ReadVertexNormal(FbxMesh* mesh, int ctrlPointIndex, int vertexCount, XMFLOAT3* normal) { if (mesh->GetElementNormalCount() GetElementNormal(0) switch (vertexNormal->GetMappingMode()) { case FbxGeometryElement::eByControlPoint: { switch (vertexNormal->GetReferenceMode()) { case FbxGeometryElement::eDirect: { normal->x = vertexNormal->GetDirectArray().GetAt(ctrlPointIndex).mData[0] normal->y = vertexNormal->GetDirectArray().GetAt(ctrlPointIndex).mData[2] normal->z = -vertexNormal->GetDirectArray().GetAt(ctrlPointIndex).mData[1] } break case FbxGeometryElement::eIndexToDirect: { int id = vertexNormal->GetIndexArray().GetAt(ctrlPointIndex) normal->x = vertexNormal->GetDirectArray().GetAt(id).mData[0] normal->y = vertexNormal->GetDirectArray().GetAt(id).mData[2] normal->z = -vertexNormal->GetDirectArray().GetAt(id).mData[1] } break default: break } } break case FbxGeometryElement::eByPolygonVertex: { switch (vertexNormal->GetReferenceMode()) { case FbxGeometryElement::eDirect: { normal->x = vertexNormal->GetDirectArray().GetAt(vertexCount).mData[0] normal->y = vertexNormal->GetDirectArray().GetAt(vertexCount).mData[2] normal->z = -vertexNormal->GetDirectArray().GetAt(vertexCount).mData[1] } break case FbxGeometryElement::eIndexToDirect: { int id = vertexNormal->GetIndexArray().GetAt(vertexCount) normal->x = vertexNormal->GetDirectArray().GetAt(id).mData[0] normal->y = vertexNormal->GetDirectArray().GetAt(id).mData[2] normal->z = -vertexNormal->GetDirectArray().GetAt(id).mData[1] } break default: break } break } } }

3. Касательная к вершинам (в соответствии с преобразованием положения вершины)

void ImportFBX::ReadVertexTangent(FbxMesh* mesh, int ctrlPointIndex, int vertexCount, XMFLOAT3* tangent) { if (mesh->GetElementTangentCount() GetElementTangent(0) switch (vertexTangent->GetMappingMode()) { case FbxGeometryElement::eByControlPoint: { switch (vertexTangent->GetReferenceMode()) { case FbxGeometryElement::eDirect: { tangent->x = vertexTangent->GetDirectArray().GetAt(ctrlPointIndex).mData[0] tangent->y = vertexTangent->GetDirectArray().GetAt(ctrlPointIndex).mData[2] tangent->z = -vertexTangent->GetDirectArray().GetAt(ctrlPointIndex).mData[1] } break case FbxGeometryElement::eIndexToDirect: { int id = vertexTangent->GetIndexArray().GetAt(ctrlPointIndex) tangent->x = vertexTangent->GetDirectArray().GetAt(id).mData[0] tangent->y = vertexTangent->GetDirectArray().GetAt(id).mData[2] tangent->z = -vertexTangent->GetDirectArray().GetAt(id).mData[1] } break default: break } } break case FbxGeometryElement::eByPolygonVertex: { switch (vertexTangent->GetReferenceMode()) { case FbxGeometryElement::eDirect: { tangent->x = vertexTangent->GetDirectArray().GetAt(vertexCount).mData[0] tangent->y = vertexTangent->GetDirectArray().GetAt(vertexCount).mData[2] tangent->z = -vertexTangent->GetDirectArray().GetAt(vertexCount).mData[1] } break case FbxGeometryElement::eIndexToDirect: { int id = vertexTangent->GetIndexArray().GetAt(vertexCount) tangent->x = vertexTangent->GetDirectArray().GetAt(id).mData[0] tangent->y = vertexTangent->GetDirectArray().GetAt(id).mData[2] tangent->z = -vertexTangent->GetDirectArray().GetAt(id).mData[1] } break default: break } break } } }

4. Вершина UV, u2 = u1, v2 = 1.0-v1, U не изменяется, а v перевернут (DX11 противоположен OPenGL).

void ImportFBX::ReadVertexUV(FbxMesh* mesh, int ctrlPointIndex, int uvIndex, XMFLOAT2* uv) { if (mesh->GetElementUVCount() GetElementUV(0) switch (vertexUV->GetMappingMode()) { case FbxGeometryElement::eByControlPoint: { switch (vertexUV->GetReferenceMode()) { case FbxGeometryElement::eDirect: { //Because these data are the same as Opengl coordinates, and we need to render the data in D3D11, some of the data have to be changed //v reverse uv->x = vertexUV->GetDirectArray().GetAt(ctrlPointIndex).mData[0] uv->y = 1.0f - vertexUV->GetDirectArray().GetAt(ctrlPointIndex).mData[1] } break case FbxGeometryElement::eIndexToDirect: { int id = vertexUV->GetIndexArray().GetAt(ctrlPointIndex) uv->x = vertexUV->GetDirectArray().GetAt(id).mData[0] uv->y = 1.0f - vertexUV->GetDirectArray().GetAt(id).mData[1] } break default: break } } break case FbxGeometryElement::eByPolygonVertex: { switch (vertexUV->GetReferenceMode()) { case FbxGeometryElement::eDirect: case FbxGeometryElement::eIndexToDirect: { uv->x = vertexUV->GetDirectArray().GetAt(uvIndex).mData[0] uv->y = 1.0f - vertexUV->GetDirectArray().GetAt(uvIndex).mData[1] } break default: break } break } } }

Окончательный результат рендеринга:







Справочные материалы:

【1】 Работа с FBX SDK (2)

【два】 Анализ и загрузка модели FBX на основе FBX SDK- (1)

【3】 Анализ и загрузка модели FBX на основе FBX SDK- (2)

【4】 Документация разработчика FBX 2017.1


Ссылка на источник:

https://download.csdn.net/download/qq_29523119/10351468

Будет улучшено в будущем

[1] Есть проблема с наименованием, но мне лень ее менять. Сетка должна быть изменена на SubMesh, Model на Mesh, FBXModel на Model

[2] FBXModel должен использовать древовидную структуру вместо векторного массива для управления моделью.

[3] Хеш-таблица текстуры не должна быть привязана к FBXModel, потому что несколько загруженных моделей FBX, вероятно, будут иметь одну и ту же загруженную текстуру или одно и то же имя относительного пути текстуры. Более необходимо установить глобальный класс управления текстурами, чтобы избежать дублирования ресурсов загрузки текстур.

[4] В данных вершин много избыточности. Вы можете настроить файл для хранения данных модели. FBX по-прежнему больше подходит как импортированная модель, а не как файл модели загрузки во время работы игры.