Directx11チュートリアル46のFBXSDK



Fbx Sdk Directx11 Tutorial 46



DX11の使用を開始することに関する以前の一連の記事には、 Directx11チュートリアル40ロードOBJモデル objモデルデータを読み取るブログ。ただし、objが読んだブログでは、いくつかの落とし穴があるとは言いませんでした。つまり、作成したobjパーサーは、特定のobjファイル形式しか解析できません。後で、作成したobjモデルパーサーを使用して、それが見つかったためです。私は1つまたは2つの特定のobjファイルを分析したパーサーだったので、objファイル形式は実際にクラッシュしています。実際、objファイルデータの多様性は私の想像を超えていました。したがって、すべての人にobjを使用することはお勧めしません。objに近づかないでください。 objはテキストデータを直接表示できますが、使い方は簡単ではありません。商用エンジンのペースに対応し、FBX形式を使用しています。




コンベンション、このブログ関連プログラムの対応する構造をリリースします。





BusinessEngineとFBXSDKについて

UE4とU3DのインポートされたモデルはFBXファイルであることがわかっています。つまり、FBXファイルはUE4とU3Dの中間モデルファイルです。なぜ「中間」を具体的に追加するのですか?これは、FBXモデルファイルがUE4およびU3Dのインポートされたモデルファイルのみであり、UE4およびU3Dゲームの実行時にロードされるモデルファイルではないためです。 UE4やU3Dなどのエンジンは、FBXファイルをインポートして独自のカスタムモデルファイルを生成します。どうしてこれなの? FBX SDKを使用してモデルデータを読み取った後、FBX SDKを使用してFBXモデルファイルを読み取ることによって直接取得された幾何学的データ(頂点データなど)には、特定の(場合によっては深刻な)冗長性があり、速度が十分に速くないため、FBXファイルは商用エンジンの単なる中間モデルファイルです。もちろん、DX11チュートリアルをケースとして使用します。それほど複雑にする必要はありません。 FBX SDKを直接使用して、FBXファイルのデータをレンダリングデータとして読み取ることができます。




FBXSDKの環境構成。

VS2017とFBXSDK2017.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のコンパイル済みバージョンのみを提供します。 3つの環境構成スキームがあります。 3つすべてを試したところ、VS2017でDLL構成を使用できることがわかりました。

FBXファイルの読み取り

FBXデータの読み取りに関しては、ここでは醜さを示すのではなく、前任者が書いたブログを引用するだけです。 FBX読み取りデータに関する次の3つのブログを強くお勧めします。

1.1。 FBX SDKに基づくFBXモデルの分析とロード-(1)

二。 FBX SDKに基づくFBXモデルの分析とロード-(2)

3.3。 FBX SDKの操作(2)

四。 FBXモデルファイルをDirect3Dレンダリングモデルとして解析します

これらの3つのブログをフォローして、データを読み取るFBXSDKの基本的な概念を基本的に理解してください。

ここで絡む唯一のことは、資料の読み取りインターフェイスが変更されたことです。 DX11でファイルを読み取る必要があるため、テクスチャファイルの相対パスを取得する必要があります(絶対パスは推奨されません)。使用されるインターフェースは、次のように、FBXTextureではなくFBXFileTextureです。

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に変換し、対応するFBXファイルのすべての.tgaイメージを.tgaに追加しました。 PSのjpgファイル。


FBXのデータ構造を読み取ります。

FBXノードはツリーの形で編成されていることがわかっているため、ツリーで編成することをお勧めします。もちろん、チュートリアルを簡単にするために、配列を使用してそれらを整理します。

1.頂点の構造は次のとおりです。

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


2.三角形の構造。fbxのfbxNodeノードでは、読み取られた各三角形に対応するマテリアルにインデックスが付けられたマテリアルIDがあり、対応するマテリアルにはさまざまな対応する属性(拡散、スペキュラー、およびさまざまなテクスチャ)が含まれているためです。ここで注意すべきことの1つは、マテリアルIDが対応するノードにバインドされていることです。たとえば、fbxNode1とfbxNode2の同じマテリアルID(たとえば、0または1の両方)にはまったく関係がなく、マテリアルIDはSetノードをバインドするためだけのものです。

struct Triangle { VertexPCNTT vertexs[3] int MaterialId }


3.材料構造。ここでは、マテリアルはテクスチャファイル名で直接表されています。対応するテクスチャファイル名が空の場合は、対応するテクスチャがないことを意味します。それ以外の場合は、対応するテクスチャがあります。

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

4.メッシュ構造。前の4つのブログからわかるように、メッシュノードによって読み取られるすべての三角形には複数のマテリアルIDがあり、メッシュタイプfbxNodeまたはfbxMeshには次のようになっているため、各マテリアルIDは対応するfbxNodeにバインドされます。メッシュの数と同じ数のマテリアルID。 (ここで三角形をメッシュにすることはできません。DrawCallは怖いので、fbxMeshデータを読み取るときに同じマテリアルIDを持つ三角形を割り当てる必要があります。同じメッシュに対して)。一般的に、fbxMeshには多くのマテリアルIDが存在するため、生成されるメッシュの数、つまり1つのメッシュに1つのマテリアルIDがあります。

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

5.モデル構造。ちょうど今、メッシュfbxNodeまたはfbxMeshが複数のメッシュを解析でき、次にfbxNodeのすべてのメッシュがモデルに解析されると言いました。ただし、モデル構造にマテリアルハッシュテーブルを追加したことに注意してください。メッシュは、独自のマテリアルIDを使用して、モデル構造内の対応するマテリアル(対応するテクスチャの相対パス)を見つけることができます。

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つずつレンダリングします。思考のプロセスは次のとおりです。

(1)メッシュの頂点キャッシュとインデックスキャッシュを設定します

(2)メッシュのマテリアルMaterialIdに従って、対応するモデルマテリアル名ハッシュテーブルで対応するマテリアルを検索し、最後に、FBXModelマテリアルハッシュテーブルで対応するID3D11ShaderResourceView *リソースを検索し、対応する4つのテクスチャの対応するパス名を指定します。マテリアル、そして対応するシェーダーレンダリングを使用すると、すべてが突然明らかになりました。


FBXデータ読み取りの座標系空間補正:

3DS MAXでモデリングしているため、3DSMAXの座標系空間は次のようになります。


3DS MAXの座標系空間は、Z軸の右手座標系であり、D3D11はY軸の左手座標系です。したがって、FBXによって読み取られたデータを直接レンダリングすると、モデルが横になっていることがよくあります。 X軸を中心に-90度をレンダリングする方法を使用し、読み取った頂点データを次のように変換しました。

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.頂点法線(頂点位置変換の逆行列変換)

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 SDKに基づくFBXモデルの分析とロード-(1)

【3】 FBX SDKに基づくFBXモデルの分析とロード-(2)

【4】 FBX2017.1開発者向けドキュメント


ソースリンク:

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

将来的に改善される

[1]命名に問題がありますが、私はそれを変更するのが面倒です。メッシュをSubMeshに、モデルをメッシュに、FBXModelをモデルに変更する必要があります

[2] FBXModelは、モデルを管理するために、ベクトル配列ではなくツリー構造を使用する必要があります。

[3]複数のロードされたFBXモデルは、同じロードされたテクスチャまたは同じテクスチャ相対パス名を持つ可能性があるため、テクスチャハッシュテーブルをFBXModelにバインドしないでください。テクスチャ読み込みリソースの重複を避けるために、グローバルテクスチャ管理クラスを確立する必要があります。

[4]頂点データには多くの冗長性があります。ファイルをカスタマイズして、モデルデータを保存できます。 FBXは、ゲームの実行中にモデルファイルをロードするのではなく、インポートされたモデルとしてさらに適しています。