• 基于FBX SDK的FBX模型解析与加载 -(二)


    http://blog.csdn.net/bugrunner/article/details/7211515

    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之间的连接关系,代码如下:

    [cpp] view plain copy
    1. void ConnectMaterialToMesh(KFbxMesh* pMesh , int triangleCount , int* pTriangleMtlIndex)  
    2. {  
    3.     // Get the material index list of current mesh  
    4.     KFbxLayerElementArrayTemplate<int>* pMaterialIndices;  
    5.     KFbxGeometryElement::EMappingMode   materialMappingMode = KFbxGeometryElement::eNONE;  
    6.   
    7.     if(pMesh->GetElementMaterial())  
    8.     {  
    9.         pMaterialIndices    = &pMesh->GetElementMaterial()->GetIndexArray();  
    10.         materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode();  
    11.         if(pMaterialIndices)  
    12.         {  
    13.             switch(materialMappingMode)  
    14.             {  
    15.             case KFbxGeometryElement::eBY_POLYGON:  
    16.                 {  
    17.                     if(pMaterialIndices->GetCount() == triangleCount)  
    18.                     {  
    19.                         for(int triangleIndex = 0 ; triangleIndex < triangleCount ; ++triangleIndex)  
    20.                         {  
    21.                             int materialIndex = pMaterialIndices->GetAt(triangleIndex);  
    22.   
    23.                             pTriangleMtlIndex[triangleIndex] = materialIndex;  
    24.                         }  
    25.                     }  
    26.                 }  
    27.                 break;  
    28.   
    29.             case KFbxGeometryElement::eALL_SAME:  
    30.                 {  
    31.                     int lMaterialIndex = pMaterialIndices->GetAt(0);  
    32.   
    33.                     for(int triangleIndex = 0 ; triangleIndex < triangleCount ; ++triangleIndex)  
    34.                     {  
    35.                         int materialIndex = pMaterialIndices->GetAt(triangleIndex);  
    36.   
    37.                         pTriangleMtlIndex[triangleIndex] = materialIndex;  
    38.                     }  
    39.                 }  
    40.             }  
    41.         }  
    42.     }  
    43. }  

    其中上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

    当然,在实际应用中这些属性并不一定需要全部读取,可以根据情况选择读取即可。材质的读取代码如下所述(简略版):

    [cpp] view plain copy
    1. void LoadMaterial(KFbxMesh* pMesh)  
    2. {  
    3.     int materialCount;  
    4.     KFbxNode* pNode;  
    5.   
    6.     if(pMesh && pMesh->GetNode())  
    7.     {  
    8.         pNode         = pMesh->GetNode();  
    9.         materialCount = pNode->GetMaterialCount();  
    10.     }  
    11.   
    12.     if(materialCount > 0)  
    13.     {  
    14.         for(int materialIndex = 0 ; materialIndex < materialCount ; materialIndex++)  
    15.         {  
    16.             KFbxSurfaceMaterial* pSurfaceMaterial = pNode->GetMaterial(materialIndex);  
    17.   
    18.             LoadMaterialAttribute(pSurfaceMaterial);  
    19.         }  
    20.     }  
    21. }  
    [cpp] view plain copy
    1. void LoadMaterialAttribute(KFbxSurfaceMaterial* pSurfaceMaterial)  
    2. {  
    3.     // Get the name of material  
    4.     pSurfaceMaterial->GetName();  
    5.   
    6.     // Phong material  
    7.     if(pSurfaceMaterial->GetClassId().Is(KFbxSurfacePhong::ClassId))  
    8.     {  
    9.         // Ambient Color  
    10.         fbxDouble3 = ((KFbxSurfacePhong*)pSurfaceMaterial)->Ambient;  
    11.         // ...  
    12.   
    13.         // Diffuse Color  
    14.         fbxDouble3 =((KFbxSurfacePhong*)pSurfaceMaterial)->Diffuse;  
    15.         // ...  
    16.   
    17.         // Specular Color  
    18.         fbxDouble3 =((KFbxSurfacePhong*)pSurfaceMaterial)->Specular;  
    19.         // ...  
    20.   
    21.         // Emissive Color  
    22.         fbxDouble3 =((KFbxSurfacePhong*)pSurfaceMaterial)->Emissive;  
    23.         // ...  
    24.   
    25.         // Opacity  
    26.         fbxDouble1 =((KFbxSurfacePhong*)pSurfaceMaterial)->TransparencyFactor;  
    27.         // ...  
    28.   
    29.         // Shininess  
    30.         fbxDouble1 =((KFbxSurfacePhong*)pSurfaceMaterial)->Shininess;  
    31.         // ...  
    32.   
    33.         // Reflectivity  
    34.         fbxDouble1 =((KFbxSurfacePhong*)pSurfaceMaterial)->ReflectionFactor;  
    35.         // ...  
    36.         return;  
    37.     }  
    38.   
    39.     // Lambert material  
    40.     if(pSurfaceMaterial->GetClassId().Is(KFbxSurfaceLambert::ClassId))  
    41.     {  
    42.   
    43.         // Ambient Color  
    44.         fbxDouble3=((KFbxSurfaceLambert*)pSurfaceMaterial)->Ambient;  
    45.         // ...  
    46.   
    47.         // Diffuse Color  
    48.         fbxDouble3 =((KFbxSurfaceLambert*)pSurfaceMaterial)->Diffuse;  
    49.         // ...  
    50.   
    51.         // Emissive Color  
    52.         fbxDouble3 =((KFbxSurfaceLambert*)pSurfaceMaterial)->Emissive;  
    53.         // ...  
    54.   
    55.         // Opacity  
    56.         fbxDouble1 =((KFbxSurfaceLambert*)pSurfaceMaterial)->TransparencyFactor;  
    57.         // ...  
    58.         return;  
    59.     }  
    60. }  

    上述代码就可以完成对普通属性加载。另外,材质中关联的Texture也需要进行加载,这个操作一般与一个纹理管理器结合起来进行,以便对所有的Texture与Material之间形成合理的关联,这一步的操作一般如下代码所述:

    [cpp] view plain copy
    1. void LoadMaterialTexture(KFbxSurfaceMaterial* pSurfaceMaterial)  
    2. {  
    3.     int textureLayerIndex;  
    4.     KFbxProperty pProperty;  
    5.     int texID;  
    6.     MaterialTextureDesc::MtlTexTypeEnum texType;  
    7.   
    8.     for(textureLayerIndex = 0 ; textureLayerIndex < KFbxLayerElement::LAYERELEMENT_TYPE_TEXTURE_COUNT ; ++textureLayerIndex)  
    9.     {  
    10.         pProperty = pSurfaceMaterial->FindProperty(KFbxLayerElement::TEXTURE_CHANNEL_NAMES[textureLayerIndex]);  
    11.         if(pProperty.IsValid())  
    12.         {  
    13.             int textureCount = pProperty.GetSrcObjectCount(KFbxTexture::ClassId);  
    14.   
    15.             for(int j = 0 ; j < textureCount ; ++j)  
    16.             {  
    17.                 KFbxTexture* pTexture = KFbxCast<KFbxTexture>(pProperty.GetSrcObject(KFbxTexture::ClassId,j));  
    18.                 if(pTexture)  
    19.                 {  
    20.                     // Use pTexture to load the attribute of current texture...  
    21.                 }  
    22.             }  
    23.         }  
    24.     }  
    25. }  

    5.3 硬件相关的材质与Effect

    有过建模经验的童鞋都知道,在3D Max或Maya中可以为某些材质指定特定的Shader来完成特定的效果,这些模型在保存时也会保存相应的硬件相关的Shader到FBX模型中,因而针对这样属性的材质也需要特别的代码来进行加载。FBX里边支持嵌入CG、HLSL、GLSL等主流着色语言,而着色语言的类型在解析时也很容易得到。

    [cpp] view plain copy
    1. void LoadMaterialAttribute(KFbxSurfaceMaterial* pSurfaceMaterial)  
    2. {  
    3.     KFbxImplementation* pImplementation;  
    4.     KString implemenationType;  
    5.   
    6.     pImplementation = GetImplementation(pSurfaceMaterial , ImplementationHLSL);  
    7.     KString implemenationType = "HLSL";  
    8.   
    9.     if(pImplementation)  
    10.     {  
    11.         LoadMaterialEffect(pSurfaceMaterial , pImplementation , &implemenationType);  
    12.     }  
    13. }  

    上述代码可以与前面的Material属性读取的代码合并。FBX一般通过一个类型为KFbxImplementation的对象将硬件相关的Shader与Material进行关联,可以使用如上的代码实现两者之间关联的情况的获取,其中ImplementationHLSL为一个标识HLSL类型Shader的宏,若是CG则用ImplementationCGFX。如果当前Material中包含了HLSL类型Shader之后,那么就可以得到一个不为空的KFbxImplementation类型的指针,在其中就可以解析该Shader的属性,否则,则该指针为空,说明些材质关联了其它类似的Shader或是不包含Shader。通过KFbxImplementation来获取Effect对应的属性的代码如下所示:

    [cpp] view plain copy
    1. void LoadMaterialEffect(KFbxSurfaceMaterial* pSurfaceMaterial , const KFbxImplementation* pImplementation , KString* pImplemenationType)  
    2. {  
    3.     KFbxBindingTable const* lRootTable = pImplementation->GetRootTable();  
    4.     fbxString lFileName                = lRootTable->DescAbsoluteURL.Get();  
    5.     fbxString lTechniqueName           = lRootTable->DescTAG.Get();  
    6.   
    7.     // Name of the effect file  
    8.     lFileName.Buffer();  
    9.   
    10.     KFbxBindingTable const* pBTable = pImplementation->GetRootTable();  
    11.     size_t entryCount = pBTable->GetEntryCount();  
    12.   
    13.     for(size_t i = 0 ; i < entryCount ; ++i)  
    14.     {  
    15.         const KFbxBindingTableEntry& btEntry = pBTable->GetEntry(i);  
    16.         const char* pEntrySrcType = btEntry.GetEntryType(true);  
    17.         KFbxProperty fbxProperty;  
    18.   
    19.         // Name of Parameter  
    20.         btEntry.GetDestination();  
    21.   
    22.         // Semantic of Parameter  
    23.         btEntry.GetDestination();  
    24.   
    25.         if(strcmp(KFbxPropertyEntryView::sEntryType , pEntrySrcType) == 0)  
    26.         {  
    27.             fbxProperty = pSurfaceMaterial->FindPropertyHierarchical(btEntry.GetSource());  
    28.             if(!fbxProperty.IsValid())  
    29.             {  
    30.                 fbxProperty = pSurfaceMaterial->RootProperty.FindHierarchical(btEntry.GetSource());  
    31.             }  
    32.         }  
    33.         else  
    34.         {  
    35.             if(strcmp(KFbxConstantEntryView::sEntryType , pEntrySrcType) == 0)  
    36.             {  
    37.                 fbxProperty = pImplementation->GetConstants().FindHierarchical(btEntry.GetSource());  
    38.             }  
    39.         }  
    40.   
    41.         if(fbxProperty.IsValid())  
    42.         {  
    43.             if(fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxTexture)) > 0)  
    44.             {  
    45.                 // Texture Parameter  
    46.                 for(int j = 0 ; j < fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxFileTexture)) ; ++j)  
    47.                 {  
    48.                     KFbxFileTexture* pFileTexture = fbxProperty.GetSrcObject(FBX_TYPE(KFbxFileTexture) , j);  
    49.                 }  
    50.   
    51.                 for(int j = 0 ; j < fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxLayeredTexture)) ; ++j)  
    52.                 {  
    53.                     KFbxLayeredTexture* pLayeredTexture = fbxProperty.GetSrcObject(FBX_TYPE(KFbxLayeredTexture) , j);  
    54.                 }  
    55.   
    56.                 for(int j = 0 ; j < fbxProperty.GetSrcObjectCount(FBX_TYPE(KFbxProceduralTexture)) ; ++j)  
    57.                 {  
    58.                     KFbxProceduralTexture* pProceduralTexture = fbxProperty.GetSrcObject(FBX_TYPE(KFbxProceduralTexture) , j);  
    59.                 }  
    60.             }  
    61.             else  
    62.             {  
    63.                 // Common Parameter  
    64.                 KFbxDataType dataType = fbxProperty.GetPropertyDataType();  
    65.   
    66.                 // Bool value  
    67.                 if(DTBool == dataType)  
    68.                 {  
    69.                     bool boolValue = KFbxGet<bool>(fbxProperty);  
    70.                 }  
    71.   
    72.                 // Integer value  
    73.                 if(DTInteger == dataType || DTEnum == dataType)  
    74.                 {  
    75.                     int intValue = KFbxGet<int>(fbxProperty);  
    76.                 }  
    77.   
    78.                 // Float  
    79.                 if(DTFloat == dataType)  
    80.                 {  
    81.                     float floatValue = KFbxGet<float>(fbxProperty);  
    82.                 }  
    83.   
    84.                 // Double  
    85.                 if(DTDouble == dataType)  
    86.                 {  
    87.                     double doubleValue = (float)KFbxGet<double>(fbxProperty);  
    88.                 }  
    89.   
    90.                 // Double2  
    91.                 if(DTDouble2 == dataType)  
    92.                 {  
    93.                     fbxDouble2 lDouble2 = KFbxGet<fbxDouble2>(fbxProperty);  
    94.                     D3DXVECTOR2 double2Value = D3DXVECTOR2((float)lDouble2[0] , (float)lDouble2[1]);  
    95.                 }  
    96.   
    97.                 // Double3  
    98.                 if(DTDouble3  == dataType || DTVector3D == dataType || DTColor3 == dataType)  
    99.                 {  
    100.                     fbxDouble3 lDouble3 = KFbxGet<fbxDouble3>(fbxProperty);  
    101.                     D3DXVECTOR3 double3Value = D3DXVECTOR3((float)lDouble3[0] , (float)lDouble3[1] , (float)lDouble3[2]);  
    102.                 }  
    103.   
    104.                 // Double4  
    105.                 if(DTDouble4  == dataType || DTVector4D == dataType || DTColor4 == dataType)  
    106.                 {  
    107.                     fbxDouble4 lDouble4 = KFbxGet<fbxDouble4>(fbxProperty);  
    108.                     D3DXVECTOR4 double4Value = D3DXVECTOR4((float)lDouble4[0] , (float)lDouble4[1] , (float)lDouble4[2] , (float)lDouble4[3]);  
    109.                 }  
    110.   
    111.                 // Double4x4  
    112.                 if(DTDouble44 == dataType)  
    113.                 {  
    114.                     fbxDouble44 lDouble44 = KFbxGet<fbxDouble44>(fbxProperty);  
    115.   
    116.                     D3DXMATRIX double4x4Value;  
    117.   
    118.                     for(int i = 0 ; i < 4 ; ++i)  
    119.                     {  
    120.                         for(int j = 0 ; j < 4 ; ++j)  
    121.                         {  
    122.                             double4x4Value.m[i][j] = (float)lDouble44[i][j];  
    123.                         }  
    124.                     }  
    125.                 }  
    126.   
    127.                 // String  
    128.                 if(DTString == dataType || DTUrl == dataType || DTXRefUrl == dataType)  
    129.                 {  
    130.                     char* pStringBuffer =(KFbxGet<fbxString>(fbxProperty)).Buffer();  
    131.                 }  
    132.             }  
    133.         }  
    134.     }  
    135. }  

    可以解析到的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对其进行再排序并重新组织几何数据间的次序。

    完成上述加载之后即可实现带有材质的渲染效果:

  • 相关阅读:
    5、Hystrix服务熔断(服务端)与服务降级(客户端)
    迷宫回溯问题
    4、负载均衡:Ribbon、Feign
    PHP-删除排序数组中的重复项
    MYSQL-连续出现的数字
    PHP
    MYSQL分数排名
    MYSQL查询第二高的薪水
    PHP算法之有效的括号
    PHP算法之电话号码的字母组合
  • 原文地址:https://www.cnblogs.com/nafio/p/9137422.html
Copyright © 2020-2023  润新知