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

  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. if(pMesh->GetElementMaterial())
  7. {
  8. pMaterialIndices = &pMesh->GetElementMaterial()->GetIndexArray();
  9. materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode();
  10. if(pMaterialIndices)
  11. {
  12. switch(materialMappingMode)
  13. {
  14. case KFbxGeometryElement::eBY_POLYGON:
  15. {
  16. if(pMaterialIndices->GetCount() == triangleCount)
  17. {
  18. for(int triangleIndex = 0 ; triangleIndex < triangleCount ; ++triangleIndex)
  19. {
  20. int materialIndex = pMaterialIndices->GetAt(triangleIndex);
  21. pTriangleMtlIndex[triangleIndex] = materialIndex;
  22. }
  23. }
  24. }
  25. break;
  26. case KFbxGeometryElement::eALL_SAME:
  27. {
  28. int lMaterialIndex = pMaterialIndices->GetAt(0);
  29. for(int triangleIndex = 0 ; triangleIndex < triangleCount ; ++triangleIndex)
  30. {
  31. int materialIndex = pMaterialIndices->GetAt(triangleIndex);
  32. pTriangleMtlIndex[triangleIndex] = materialIndex;
  33. }
  34. }
  35. }
  36. }
  37. }
  38. }

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

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

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

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

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

5.3 硬件相关的材质与Effect

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

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

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

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

可以解析到的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模型解析与加载 -(二)相关推荐

  1. 基于FBX SDK的FBX模型解析与加载 -(三)

    6. 加载Camera和Light 在FBX模型中除了几何数据外较为常用的信息可能就是Camera和Light,虽然在游戏中一般不直接从模型中得到这两部分信息,而是由引擎来提供,但是FBX中提供了对这 ...

  2. Html监听Fbx文件加载,FBX格式mesh解析与加载(一)

    FBX格式mesh解析与加载(一) FBX格式mesh解析与加载(一) ** 理解FBX格式中Mesh数据结构** fbx文件是现在许多建模动画软件和游戏引擎之间共用的模型文件格式.fbx文件分为两种 ...

  3. 微信小程序实现FBX模型的动画加载

    鉴于有CSDN友问我FBX模型在小程序端加载的问题,我就在这里给大家介绍一下吧~ 首先,加载fbx模型,我们用到的是three.js和不同的模型类型的加载库,那么,我们在得到了web版本的加载库的前提 ...

  4. nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)

    文章目录 一.nuScenes数据集格式精解 二.nuScenes数据格式转换(To COCO) 数据格式转换框架 2.1 核心:convert_nuScenes.py解析 其他格式转换文件 2.1. ...

  5. nuScenes自动驾驶数据集:数据格式精解,格式转换,模型的数据加载 (一)

    nuScenes数据集及nuScenes开发工具包简介 文章目录 nuScenes数据集及nuScenes开发工具包简介 1.1. nuScenes数据集简介: 1.2 数据采集: 1.2.1 传感器 ...

  6. pytorch模型保存与加载总结

    pytorch模型保存与加载总结 模型保存与加载方式 模型保存 方式一 只存储模型中的参数,该方法速度快,占用空间少(官方推荐使用) model = VGGNet() torch.save(model ...

  7. 人工智能算法之梯度下降法、协同过滤、相似度技术、ALS算法(附案例分析)、模型存储与加载、推荐系统的冷启动问题

    梯度下降法 求解机器学习算法的模型参数,即无约束优化问题时,梯度下降法是最常采用的方法之一,另一种常用的方法是最小二乘法.这里对梯度下降法做简要介绍. 最小二乘法法适用于模型方程存在解析解的情况.如果 ...

  8. tensor和模型 保存与加载 PyTorch

    PyTorch教程-7:PyTorch中保存与加载tensor和模型详解 保存和读取Tensor PyTorch中的tensor可以保存成 .pt 或者 .pth 格式的文件,使用torch.save ...

  9. Spring解析,加载及实例化Bean的顺序(零配置)

    点击上方蓝色"方志朋",选择"设为星标"回复"666"获取独家整理的学习资料! 作者:jb_hz blog.csdn.net/qq_2752 ...

  10. 页面加载完毕_【前端面试】dom 的解析,加载,渲染

    本文会把 dom 的解析,加载,渲染结合 window.performance 一起讲. dom 的解析 解析:HTMl 解析器把 HTML 构建成 HTML 树形数据结构,也就是 DOM 树. 注意 ...

最新文章

  1. 图片的另一种展现—将后台图片编码直接展现为图片
  2. Apache的安装与下载
  3. java 好用的工具类
  4. 基于EM算法的高斯混合模型参数估计
  5. 新闻发布项目——实体类(newsTb)
  6. 使用阿基米德螺线进行数据可视化
  7. if判断用户名 linux,Shell脚本IF条件判断和判断条件总结
  8. pycharm和mysql_数据库 mysql 和 pycharm交互
  9. pandas 计算工具
  10. 【IMX6ULL笔记】--内核底层驱动初步探究
  11. 尚硅谷宋红康java基础学习笔记
  12. 使用win10远程控制ubuntu14.04(2)
  13. 手机滚动截屏软件推荐
  14. pwm超详细解读,大佬细说pwm的控制方式
  15. 顶级二级三级域名的区别
  16. Linux下:matplotlib 添加 Times New Roman字体
  17. JavaScript实现页面倒计时效果
  18. Authority-check基础知识
  19. Spring05:面向切面编程
  20. 频率域滤波matlab函数,6、频率域滤波

热门文章

  1. 一眼看出二叉树中序遍历结果的诀窍
  2. 整理牛人看文献的方法
  3. 西安网络安全周 | 联软科技:论端点安全在零信任体系中的重要性
  4. oCPC实践录 | 好难理解的oCPC成本控制算法(1)
  5. 机器学习常问问题集锦(持续更新中)
  6. 如何自制daplink_DAPLINK功能介绍
  7. 摄影曝光基础——光圈、快门、ISO
  8. 杰奇cms,杰奇cms程序,杰奇cms建站方法
  9. 网络安全——ARP欺骗与突破封锁
  10. linux系统下使用uTorrent下载ipv6资源