You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中按多边形材质拆分FBX网格数据的问题求助

按材质拆分FBX网格数据(解决重复顶点与索引错误)

问题背景

我有一个FBXMeshData类存储FBX网格数据,包含顶点数据(位置、UV、颜色等)、材质列表、三角面索引数组,以及每个三角面对应的材质索引数组。需要将该网格按材质拆分为多个FBXMeshData实例,但当前实现存在重复顶点和索引错乱的问题。

问题根源

拆分时直接复用原顶点索引是错误的——每个新网格的顶点仅包含当前材质组用到的顶点,原索引无法直接对应新的顶点列表;同时未建立原顶点到新顶点的映射,导致重复添加顶点或索引指向错误位置。

正确实现思路

  1. 特殊情况处理:无材质或仅一种材质时直接返回原网格
  2. 按材质索引分组所有三角面
  3. 对每个材质组:
    • 收集该组所有三角面用到的原顶点索引
    • 建立原顶点索引到新顶点列表的映射(自动去重)
    • 根据映射生成新的顶点数据和重新计算的三角面索引
    • 创建新的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 09:43:39