C#中按多边形材质拆分FBX网格数据的问题求助
按材质拆分FBX网格数据(解决重复顶点与索引错误)
问题背景
我有一个FBXMeshData类存储FBX网格数据,包含顶点数据(位置、UV、颜色等)、材质列表、三角面索引数组,以及每个三角面对应的材质索引数组。需要将该网格按材质拆分为多个FBXMeshData实例,但当前实现存在重复顶点和索引错乱的问题。
问题根源
拆分时直接复用原顶点索引是错误的——每个新网格的顶点仅包含当前材质组用到的顶点,原索引无法直接对应新的顶点列表;同时未建立原顶点到新顶点的映射,导致重复添加顶点或索引指向错误位置。
正确实现思路
- 特殊情况处理:无材质或仅一种材质时直接返回原网格
- 按材质索引分组所有三角面
- 对每个材质组:
- 收集该组所有三角面用到的原顶点索引
- 建立原顶点索引到新顶点列表的映射(自动去重)
- 根据映射生成新的顶点数据和重新计算的三角面索引
- 创建新的
FBXMeshData实例,赋值对应材质和新数据
代码实现
FBXMeshData类定义
public class FBXMeshData { // 顶点数据:每个元素包含位置、UV、颜色等 public List<Vertex> VertexData { get; set; } = new List<Vertex>(); // 网格材质列表 public List<Material> Materials { get; set; } = new List<Material>(); // 三角面索引:每3个元素代表一个三角面的顶点索引 public List<int> VertexIndices { get; set; } = new List<int>(); // 每个三角面对应的材质索引(与VertexIndices按每3个元素对应) public List<int> MaterialIndices { get; set; } = new List<int>(); // 顶点结构体示例 public struct Vertex { public Vector3 Position; public Vector2 UV; public Color Color; // 可添加其他顶点属性:法线、切线等 } // 材质类示例 public class Material { public string Name; public Color DiffuseColor; // 其他材质属性 } }
按材质拆分的正确实现
public static List<FBXMeshData> SplitByMaterial(FBXMeshData originalMesh) { var result = new List<FBXMeshData>(); // 特殊情况:无材质或仅一种材质,直接返回原网格(可选择克隆避免引用问题) if (originalMesh.Materials.Count <= 1) { // 若需要独立实例,可在这里克隆原网格数据 result.Add(originalMesh); return result; } // 步骤1:按材质索引分组三角面(每个三角面对应MaterialIndices中的一个元素,对应3个顶点索引) var materialFaceGroups = new Dictionary<int, List<int>>(); for (int i = 0; i < originalMesh.MaterialIndices.Count; i++) { int matIndex = originalMesh.MaterialIndices[i]; if (!materialFaceGroups.ContainsKey(matIndex)) { materialFaceGroups[matIndex] = new List<int>(); } // 添加当前三角面的3个顶点索引(i*3到i*3+2) materialFaceGroups[matIndex].AddRange(originalMesh.VertexIndices.GetRange(i * 3, 3)); } // 步骤2:为每个材质组创建新网格 foreach (var group in materialFaceGroups) { int matIndex = group.Key; var originalFaceIndices = group.Value; // 建立原顶点索引到新顶点列表的映射(去重) var vertexMap = new Dictionary<int, int>(); var newVertices = new List<FBXMeshData.Vertex>(); var newIndices = new List<int>(); foreach (int originalVertexIndex in originalFaceIndices) { if (!vertexMap.ContainsKey(originalVertexIndex)) { vertexMap[originalVertexIndex] = newVertices.Count; newVertices.Add(originalMesh.VertexData[originalVertexIndex]); } // 添加映射后的新索引 newIndices.Add(vertexMap[originalVertexIndex]); } // 创建新网格实例 var newMesh = new FBXMeshData { VertexData = newVertices, Materials = new List<FBXMeshData.Material> { originalMesh.Materials[matIndex] }, VertexIndices = newIndices, MaterialIndices = Enumerable.Repeat(0, newIndices.Count / 3).ToList() // 新网格仅一种材质,索引全为0 }; result.Add(newMesh); } return result; }
FBX SDK获取材质索引的注意事项
从FBX SDK读取时,需确保MaterialIndices的数量与三角面数量一致(每个三角面对应一个材质索引),避免索引错位:
// FBX SDK C++示例:获取每个三角面的材质索引 FbxMesh* pMesh = ...; int polygonCount = pMesh->GetPolygonCount(); std::vector<int> materialIndices; for (int i = 0; i < polygonCount; i++) { int matIndex = pMesh->GetPolygonMaterial(i); materialIndices.push_back(matIndex); }
注意:FBX中材质索引可能为-1(无材质),需提前处理为0或其他有效索引,避免分组出错。
关键注意点
- 顶点去重:通过
vertexMap确保同一顶点在新网格中只出现一次,避免重复顶点 - 索引重映射:所有三角面索引必须基于新的顶点列表重新计算,不能直接复用原索引
- 材质对应:新网格的
MaterialIndices统一设为0,因为每个新网格仅包含一种材质 - 克隆原数据:若需要原网格保持不变,拆分时应克隆顶点和材质数据,避免引用同一对象
内容的提问来源于stack exchange,提问作者anubis
相关产品推荐
相关产品推荐

