1. 程式人生 > >基於FBX SDK的FBX模型解析與載入 -(二)

基於FBX SDK的FBX模型解析與載入 -(二)

5. 載入材質 

Material是一個模型渲染時必不可少的部分,當然,這些資訊也被存到了FBX之中(甚至各種貼圖等也可以直接內嵌到FBX內部),就需要從FBX中載入這些資訊以完成帶有材質的渲染。材質的載入可以與Mesh的載入相結合來完成,但更好的方法是獨立進行,這樣各模組間的關係更清晰,但這就需要一個額外的操作,那就是關聯Mesh與Material。FBX中的材質物件包含了豐富的資訊,比如最常規的從Max中可以看到那些材質屬性,如ambient、diffuse、specular的color和texture;shininess、opacity值等,更高階一點的屬性諸如Effect的引數、原始檔等都可以儲存。它是儘可能保證從建模工具中匯出時不丟失地儲存材質資訊,但我們在使用時卻可以有選擇地讀取。

5.1 關聯Mesh與材質

對於Material與Mesh獨立載入的系統而言,首先需要讀取相關的資訊將兩者關聯起來,這些資訊其實對也都儲存在KFbxMesh之內(屬於幾何資訊的一部分吧)。每個帶有材質的Mesh結點上都會包含有一個型別為KFbxGeometryElementMaterial的結點(若不含有材質則該結點為空),該結點中記錄了Mesh中的多邊形(這裡全部為三角形)與每個材質的對應關係,讀取該結點中的資訊建立Mesh與Material之間的連線關係,程式碼如下:

void ConnectMaterialToMesh(KFbxMesh* pMesh , int triangleCount , int* pTriangleMtlIndex)
{
	// Get the material index list of current mesh
	KFbxLayerElementArrayTemplate<int>* pMaterialIndices;
	KFbxGeometryElement::EMappingMode   materialMappingMode = KFbxGeometryElement::eNONE;

	if(pMesh->GetElementMaterial())
	{
		pMaterialIndices    = &pMesh->GetElementMaterial()->GetIndexArray();
		materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode();
		if(pMaterialIndices)
		{
			switch(materialMappingMode)
			{
			case KFbxGeometryElement::eBY_POLYGON:
				{
					if(pMaterialIndices->GetCount() == triangleCount)
					{
						for(int triangleIndex = 0 ; triangleIndex < triangleCount ; ++triangleIndex)
						{
							int materialIndex = pMaterialIndices->GetAt(triangleIndex);

							pTriangleMtlIndex[triangleIndex] = materialIndex;
						}
					}
				}
				break;

			case KFbxGeometryElement::eALL_SAME:
				{
					int lMaterialIndex = pMaterialIndices->GetAt(0);

					for(int triangleIndex = 0 ; triangleIndex < triangleCount ; ++triangleIndex)
					{
						int materialIndex = pMaterialIndices->GetAt(triangleIndex);

						pTriangleMtlIndex[triangleIndex] = materialIndex;
					}
				}
			}
		}
	}
}

其中上triangleCount即為從pMesh中讀取得到的三角形的數量,pTriangleMtlIndex是一個長度為triangleCount的陣列,主要用來儲存讀取到的三角形對應的材質索引。注意:這裡考慮的情況是對於一個三角形只對應一個材質,而一般情況下也是這樣(如果是對應多個材質的話需要些許修改此處的程式碼)。完成Mesh的索引讀取之後即可以將pTriangleMtlIndex中的值以合適的方式轉儲到對應的三角形列表中(或以其它的方式對應)以便在渲染時使用。

5.2 普通材質

FBX中實際儲存材質資訊的位置是每個Mesh中對應的一個型別為KFbxSurfaceMaterial的結點,其裡邊儲存了普通材質的典型資訊,主要包括以下屬性(有一些沒有列出):

  •   ShadingModel                 材質的光照模型,一般為兩種典型的區域性光照模型:Phong、Lambert 
  •   Emissive                          Emissive屬性 
  •   EmissiveFactor
  •   Ambient                           Ambient屬性
  •   AmbientFactor
  •   Diffuse                             Diffuse屬性
  •   DiffuseFactor
  •   Specular                           Specular屬性
  •   SpecularFactor
  •   Shininess                         Sepcular的Shininess屬性
  •   Bump                               Normal Map相關的屬性
  •   NormalMap
  •   BumpFactor
  •   TransparentColor             Transparent屬性
  •   TransparencyFactor
  •   Reflection                        Reflection屬性
  •   ReflectionFactor

當然,在實際應用中這些屬性並不一定需要全部讀取,可以根據情況選擇讀取即可。材質的讀取程式碼如下所述(簡略版):

void LoadMaterial(KFbxMesh* pMesh)
{
	int materialCount;
	KFbxNode* pNode;

	if(pMesh && pMesh->GetNode())
	{
		pNode         = pMesh->GetNode();
		materialCount = pNode->GetMaterialCount();
	}

	if(materialCount > 0)
	{
		for(int materialIndex = 0 ; materialIndex < materialCount ; materialIndex++)
		{
			KFbxSurfaceMaterial* pSurfaceMaterial = pNode->GetMaterial(materialIndex);

			LoadMaterialAttribute(pSurfaceMaterial);
		}
	}
}
void LoadMaterialAttribute(KFbxSurfaceMaterial* pSurfaceMaterial)
{
	// Get the name of material
	pSurfaceMaterial->GetName();

	// Phong material
	if(pSurfaceMaterial->GetClassId().Is(KFbxSurfacePhong::ClassId))
	{
		// Ambient Color
		fbxDouble3 = ((KFbxSurfacePhong*)pSurfaceMaterial)->Ambient;
		// ...

		// Diffuse Color
		fbxDouble3 =((KFbxSurfacePhong*)pSurfaceMaterial)->Diffuse;
		// ...

		// Specular Color
		fbxDouble3 =((KFbxSurfacePhong*)pSurfaceMaterial)->Specular;
		// ...

		// Emissive Color
		fbxDouble3 =((KFbxSurfacePhong*)pSurfaceMaterial)->Emissive;
		// ...

		// Opacity
		fbxDouble1 =((KFbxSurfacePhong*)pSurfaceMaterial)->TransparencyFactor;
		// ...

		// Shininess
		fbxDouble1 =((KFbxSurfacePhong*)pSurfaceMaterial)->Shininess;
		// ...

		// Reflectivity
		fbxDouble1 =((KFbxSurfacePhong*)pSurfaceMaterial)->ReflectionFactor;
		// ...
		return;
	}

	// Lambert material
	if(pSurfaceMaterial->GetClassId().Is(KFbxSurfaceLambert::ClassId))
	{

		// Ambient Color
		fbxDouble3=((KFbxSurfaceLambert*)pSurfaceMaterial)->Ambient;
		// ...

		// Diffuse Color
		fbxDouble3 =((KFbxSurfaceLambert*)pSurfaceMaterial)->Diffuse;
		// ...

		// Emissive Color
		fbxDouble3 =((KFbxSurfaceLambert*)pSurfaceMaterial)->Emissive;
		// ...

		// Opacity
		fbxDouble1 =((KFbxSurfaceLambert*)pSurfaceMaterial)->TransparencyFactor;
		// ...
		return;
	}
}

上述程式碼就可以完成對普通屬性載入。另外,材質中關聯的Texture也需要進行載入,這個操作一般與一個紋理管理器結合起來進行,以便對所有的Texture與Material之間形成合理的關聯,這一步的操作一般如下程式碼所述:

void LoadMaterialTexture(KFbxSurfaceMaterial* pSurfaceMaterial)
{
	int textureLayerIndex;
	KFbxProperty pProperty;
	int texID;
	MaterialTextureDesc::MtlTexTypeEnum texType;

	for(textureLayerIndex = 0 ; textureLayerIndex < KFbxLayerElement::LAYERELEMENT_TYPE_TEXTURE_COUNT ; ++textureLayerIndex)
	{
		pProperty = pSurfaceMaterial->FindProperty(KFbxLayerElement::TEXTURE_CHANNEL_NAMES[textureLayerIndex]);
		if(pProperty.IsValid())
		{
			int textureCount = pProperty.GetSrcObjectCount(KFbxTexture::ClassId);

			for(int j = 0 ; j < textureCount ; ++j)
			{
				KFbxTexture* pTexture = KFbxCast<KFbxTexture>(pProperty.GetSrcObject(KFbxTexture::ClassId,j));
				if(pTexture)
				{
					// Use pTexture to load the attribute of current texture...
				}
			}
		}
	}
}

5.3 硬體相關的材質與Effect

有過建模經驗的童鞋都知道,在3D Max或Maya中可以為某些材質指定特定的Shader來完成特定的效果,這些模型在儲存時也會儲存相應的硬體相關的Shader到FBX模型中,因而針對這樣屬性的材質也需要特別的程式碼來進行載入。FBX裡邊支援嵌入CG、HLSL、GLSL等主流著色語言,而著色語言的型別在解析時也很容易得到。

void LoadMaterialAttribute(KFbxSurfaceMaterial* pSurfaceMaterial)
{
	KFbxImplementation* pImplementation;
	KString implemenationType;

	pImplementation = GetImplementation(pSurfaceMaterial , ImplementationHLSL);
	KString implemenationType = "HLSL";

	if(pImplementation)
	{
		LoadMaterialEffect(pSurfaceMaterial , pImplementation , &implemenationType);
	}
}

上述程式碼可以與前面的Material屬性讀取的程式碼合併。FBX一般通過一個型別為KFbxImplementation的物件將硬體相關的Shader與Material進行關聯,可以使用如上的程式碼實現兩者之間關聯的情況的獲取,其中ImplementationHLSL為一個標識HLSL型別Shader的巨集,若是CG則用ImplementationCGFX。如果當前Material中包含了HLSL型別Shader之後,那麼就可以得到一個不為空的KFbxImplementation型別的指標,在其中就可以解析該Shader的屬性,否則,則該指標為空,說明些材質關聯了其它類似的Shader或是不包含Shader。通過KFbxImplementation來獲取Effect對應的屬性的程式碼如下所示:

void LoadMaterialEffect(KFbxSurfaceMaterial* pSurfaceMaterial , const KFbxImplementation* pImplementation , KString* pImplemenationType)
{
	KFbxBindingTable const* lRootTable = pImplementation->GetRootTable();
	fbxString lFileName                = lRootTable->DescAbsoluteURL.Get();
	fbxString lTechniqueName           = lRootTable->DescTAG.Get();

	// Name of the effect file
	lFileName.Buffer();

	KFbxBindingTable const* pBTable = pImplementation->GetRootTable();
	size_t entryCount = pBTable->GetEntryCount();

	for(size_t i = 0 ; i < entryCount ; ++i)
	{
		const KFbxBindingTableEntry& btEntry = pBTable->GetEntry(i);
		const char* pEntrySrcType = btEntry.GetEntryType(true);
		KFbxProperty fbxProperty;

		// Name of Parameter
		btEntry.GetDestination();

		// Semantic of Parameter
		btEntry.GetDestination();

		if(strcmp(KFbxPropertyEntryView::sEntryType , pEntrySrcType) == 0)
		{
			fbxProperty = pSurfaceMaterial->FindPropertyHierarchical(btEntry.GetSource());
			if(!fbxProperty.IsValid())
			{
				fbxProperty = pSurfaceMaterial->RootProperty.FindHierarchical(btEntry.GetSource());
			}
		}
		else
		{
			if(strcmp(KFbxConstantEntryView::sEntryType , pEntrySrcType) == 0)
			{
				fbxProperty = pImplementation->GetConstants().FindHierarchical(btEntry.GetSource());
			}
		}

		if(fbxProperty.IsValid())
		{
			if(fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxTexture)) > 0)
			{
				// Texture Parameter
				for(int j = 0 ; j < fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxFileTexture)) ; ++j)
				{
					KFbxFileTexture* pFileTexture = fbxProperty.GetSrcObject(FBX_TYPE(KFbxFileTexture) , j);
				}

				for(int j = 0 ; j < fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxLayeredTexture)) ; ++j)
				{
					KFbxLayeredTexture* pLayeredTexture = fbxProperty.GetSrcObject(FBX_TYPE(KFbxLayeredTexture) , j);
				}

				for(int j = 0 ; j < fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxProceduralTexture)) ; ++j)
				{
					KFbxProceduralTexture* pProceduralTexture = fbxProperty.GetSrcObject(FBX_TYPE(KFbxProceduralTexture) , j);
				}
			}
			else
			{
				// Common Parameter
				KFbxDataType dataType = fbxProperty.GetPropertyDataType();

				// Bool value
				if(DTBool == dataType)
				{
					bool boolValue = KFbxGet<bool>(fbxProperty);
				}

				// Integer value
				if(DTInteger == dataType || DTEnum == dataType)
				{
					int intValue = KFbxGet<int>(fbxProperty);
				}

				// Float
				if(DTFloat == dataType)
				{
					float floatValue = KFbxGet<float>(fbxProperty);
				}

				// Double
				if(DTDouble == dataType)
				{
					double doubleValue = (float)KFbxGet<double>(fbxProperty);
				}

				// Double2
				if(DTDouble2 == dataType)
				{
					fbxDouble2 lDouble2 = KFbxGet<fbxDouble2>(fbxProperty);
					D3DXVECTOR2 double2Value = D3DXVECTOR2((float)lDouble2[0] , (float)lDouble2[1]);
				}

				// Double3
				if(DTDouble3  == dataType || DTVector3D == dataType || DTColor3 == dataType)
				{
					fbxDouble3 lDouble3 = KFbxGet<fbxDouble3>(fbxProperty);
					D3DXVECTOR3 double3Value = D3DXVECTOR3((float)lDouble3[0] , (float)lDouble3[1] , (float)lDouble3[2]);
				}

				// Double4
				if(DTDouble4  == dataType || DTVector4D == dataType || DTColor4 == dataType)
				{
					fbxDouble4 lDouble4 = KFbxGet<fbxDouble4>(fbxProperty);
					D3DXVECTOR4 double4Value = D3DXVECTOR4((float)lDouble4[0] , (float)lDouble4[1] , (float)lDouble4[2] , (float)lDouble4[3]);
				}

				// Double4x4
				if(DTDouble44 == dataType)
				{
					fbxDouble44 lDouble44 = KFbxGet<fbxDouble44>(fbxProperty);

					D3DXMATRIX double4x4Value;

					for(int i = 0 ; i < 4 ; ++i)
					{
						for(int j = 0 ; j < 4 ; ++j)
						{
							double4x4Value.m[i][j] = (float)lDouble44[i][j];
						}
					}
				}

				// String
				if(DTString == dataType || DTUrl == dataType || DTXRefUrl == dataType)
				{
					char* pStringBuffer =(KFbxGet<fbxString>(fbxProperty)).Buffer();
				}
			}
		}
	}
}

可以解析到的Effect的主要屬性包括Shader所對應的原始檔、Shader中提供的各種外部引數的初始設定等(比如在3D Max中通過UI控制元件所調節的引數的數值)。具體的方法程式碼裡邊已經比較明確了,這裡就不在贅述了。後續的一些操作就要看整個材質與Effect部分的資料結構如何組織以及如何與你自己的程式碼整合。

5.4 根據材質優化Mesh

通過FBX匯出之後得到的FBX模型在儲存時一般會以幾何屬性為首要考量因素來生成整個檔案的Scene graph,因此上述解析得到的幾何網格與Material之間的對映關係可能並不適合於直接進行繪製,一般需要重新再組織。比如其間的對映關係可能是

  1. Triangle0 -> Material1
  2. Triangle1 -> Material0
  3. Triangle2 -> Material1
  4. ...

如果一個應用的渲染流程使用了Material之間的最少切換次數來作為渲染的首要考慮的話,那麼就不能直接 使用Triangle的順序來生成渲染Buffer,而需要根據Material對其進行再排序並重新組織幾何資料間的次序。

完成上述載入之後即可實現帶有材質的渲染效果:

下一篇介紹一下動畫相關的Camera與Light的讀取...

相關推薦

基於FBX SDK的FBX模型解析載入 -

5. 載入材質  Material是一個模型渲染時必不可少的部分,當然,這些資訊也被存到了FBX之中(甚至各種貼圖等也可以直接內嵌到FBX內部),就需要從FBX中載入這些資訊以完成帶有材質的渲染。材質的載入可以與Mesh的載入相結合來完成,但更好的方法是獨立進行,這樣各模組

基於FBX SDK的FBX模型解析載入 -

6. 載入Camera和Light 在FBX模型中除了幾何資料外較為常用的資訊可能就是Camera和Light,雖然在遊戲中一般不直接從模型中得到這兩部分資訊,而是由引擎來提供,但是FBX中提供了對這些資訊儲存的支援。其實單純載入這兩部分的資訊很簡單,就像之前介紹的在整

ItemDecoration深入解析實戰—— 實際運用

一 概述 這是這個系列的第二篇文章,第一篇 ItemDecoration深入解析與實戰(一)——原始碼分析 是偏原理性的,而這篇是偏應用性的。沒看過上一篇文章對閱讀此文也基本沒多大影響,不過了解原理會加深對本文Demo的理解。 這篇文章將會實現上篇文章最後說的幾個實戰點,包括: (Lin

西瓜書《機器學習》學習筆記 模型評估選擇 效能度量 ROC AUC...

目錄 3、效能度量(performance measure) 衡量模型泛化能力的評價標準,就是效能度量。 效能度量 <————> 任務需求 在對比不同模型的“好壞”時,使用不同的效能度量往往會導致不同的結果,這也意味著模型的好壞是相

基於FBX SDK的FBX模型解析載入

1. 簡介 FBX是Autodesk的一個用於跨平臺的免費三維資料交換的格式(最早不是由Autodesk開發,但後來被其收購),目前被 眾多的標準建模軟體所支援,在遊戲開發領域也常用來作為各種建模工具的標準匯出格式。Autodesk提供了基於C++(還有Python)的SDK來實現對FBX格式的各種讀寫、

MySQL系列:基於binlog的增量訂閱消費

clas 需要 val tro ali cat tor rip 變化   在一些業務場景中,像在數據分析中我們有時候需要捕獲數據變化(CDC);在數據審計中,我們也往往需要知道數據從這個點到另一個點的變化;同樣在實時分析中,我們有時候需要看到某個值得實時變化等。 要解決以上

ItemDecoration深入解析實戰

一 概述 ItemDecoration 是 RecyclerView 中的一個抽象靜態內部類。 An ItemDecoration allows the application to add a special drawing and layout offset to specific item v

ItemDecoration深入解析實戰——原始碼分析

一 概述 ItemDecoration 是 RecyclerView 中的一個抽象靜態內部類。 An ItemDecoration allows the application to add a special drawing and layout offset to specific item v

第二章 模型的估計結果

工具變數法(IV): 是為了解決一個違反經典假設問題而設計的,假設條件是:解釋變數與隨機擾動項不相關。如果出現了違反該假設的問題,就需要找一個和解釋變數高度相關的、同時和隨機擾動項不相關的變數。要注意的問題是,工具變數的設定除了上述兩個條件以外,工具變數的個數至少要大於或者

mybatis原始碼解析之Configuration載入

概述 上一篇我們講了configuation.xml中幾個標籤的解析,例如<properties>,<typeAlises>,<settings>等,今天我們來介紹剩下的兩個比較重要的標籤之一,<environments>,這個標籤主要用於我們訪問資料庫的配置

JavaScript “跑馬燈”抽獎活動程式碼解析優化

最近的專案中做了一個“跑馬燈”的抽獎特效外掛。上篇文章已經分享過html和css 的相關知識。這篇文章主要分享一些 JavaScript 相關的知識。這幾天在寫這篇文章的時候,也順便把自己的程式碼重構了一下。 這裡主要是來寫寫自己的優化過程。俗話說:

Spring IOC原理原始碼解析(@Autowired原理詳解 :標識屬性方法)

原始碼推薦看這篇部落格的時候開啟Spring原始碼,一邊看原始碼,一邊看部落格上程式碼的關鍵處的註釋,這樣能更好的理解Spring IOC的流程及內部實現和使用方法。如果你對IOC的原理有些瞭解,則這些註釋能幫你更深入的理解其實現方式。 Spring容器在每個

模型評估選擇中篇-ROC曲線AUC曲線

P-R曲線 以二分類問題為例進行說明。分類結果的混淆矩陣如下圖所示。 假設,現在我們用某一演算法h對樣本進行二分類(劃分為正例、反例)。由於演算法可能與理想方法存在誤差,因此在劃分結果中,劃分為正例的那部分樣本中,可能存在正例,也可能存在反例。同理,

Selenium2Library原始碼解析擴充套件

一直覺得Selenium2Library對selenium的封裝很贊,最近模擬它的結構封裝給一個同事寫了個C# selenium的demo,過程中看了細看了一部分原始碼。加上之前封裝的內容,分享一波。 注1:以下涉及到RF的指令碼全未加延時sleep,如需除錯

tensorflow-模型儲存和載入

模型儲存和載入(一)TensorFlow的模型格式有很多種,針對不同場景可以使用不同的格式。格式簡介Checkpoint用於儲存模型的權重,主要用於模型訓練過程中引數的備份和模型訓練熱啟動。GraphD

基於星雲鏈的智慧合約Dapp——執行星雲鏈

上一篇文章講了搭建星雲私鏈的基本環境,接著我們來講講如何配置和執行星雲鏈。這裡講的只是一些基礎的介紹,為智慧合約和Dapp做準備,後期我們分析星雲鏈原始碼的時候再詳細講解。 創世區塊 在啟動星雲鏈之前,我們必須定義創世區塊的配置檔案。 創世區塊配置

Unity 動態載入記憶體

Unity幾種動態載入Prefab方式的差異: 其實存在3種載入prefab的方式: 一是靜態引用,建一個public的變數,在Inspector裡把prefab拉上去,用的時候instantiate 二是Resource.Load,Load以後instantiate 三是

基於物件的資料篩選排序

可能大家對於資料庫的操作太過於熟悉了,以至於忘記.Net提供的強大而靈活的資料操作。例如,當我們想對資料進行篩選時,首先想到的是“Where”而不是List< T>.FindAll();當我們想對資料進行排序時,首先想到的是“Sort”而不是List

如何用Tensorflow訓練模型成pb檔案——基於tfrecord的讀取

簡介 上一篇介紹了基於原始圖片的讀取,這一篇介紹基於TFRecord的讀取。TFRecord是TensorFlow提供的資料讀取格式,效率高。這裡不介紹TFRecord的製作過程,網上有很多,假設你已經瞭解了。 訓練 定義網路結構,與上一篇相似,不多說了,

Echarts使用心得總結——非同步資料載入更新

上次用Echarts做了一個會員增量統計圖表,正好用到了Echarts非同步資料載入,使用的是下拉聯動+Echarts資料更新,下面把實現程式碼分享給大家。 一、時間選擇器下拉聯動的實現: <div class="selects-div"> <