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

优化3D应用网格几何序列化性能的技术咨询

问题:优化3D应用序列化耗时,Assimp+OBJ性能瓶颈

我需要提升3D应用的序列化速度,测试场景下,Blender序列化耗时1秒,3DS Max耗时2秒,但我的应用耗时高达15秒。目前我采用OBJ文件格式配合Assimp进行网格序列化,发现Assimp的序列化环节耗时过高,想请教两个问题:

  1. 是否应该改用自定义序列化格式?
  2. 有哪些可以参考的优化技巧?

附上当前的序列化代码:

aiScene* scene = new aiScene();

// Allocate materials memory
scene->mNumMaterials = 1;
scene->mMaterials = new aiMaterial*[scene->mNumMaterials];

// Allocate mesh memory
scene->mNumMeshes = (unsigned)m_flatMeshContainer.size();
scene->mMeshes = new aiMesh*[m_flatMeshContainer.size()];

scene->mRootNode = new aiNode();
scene->mRootNode->mNumMeshes = 0;

// Create materials
for (Uint32 i = 0u; i < scene->mNumMaterials; ++i)
{
    scene->mMaterials[i] = new aiMaterial();
}

// Create meshes
std::mutex mtx;
tbb::parallel_for(size_t(0), m_meshGroupIdentifiers.size(), [&](size_t idx) {

    const auto& groupId = m_meshGroupIdentifiers[idx];
    const auto group = getMeshGroupPtr_FromEntity(groupId);

    // Create meshes
    const auto nbMeshes = (unsigned)group->m_meshes.size();
    aiNode* node = new aiNode();
    node->mMeshes = new unsigned int[nbMeshes];
    node->mNumMeshes = nbMeshes;

    for (Uint32 i = 0u; i < nbMeshes; ++i)
    {
        // Init mesh
        const Mesh* nativeMesh = group->m_meshes[i];
        const auto flatMeshIdx = nativeMesh->getFlatId();
        scene->mMeshes[flatMeshIdx] = new aiMesh();
        node->mMeshes[i] = flatMeshIdx;

        aiMesh* mesh = scene->mMeshes[flatMeshIdx];
        mesh->mName = group->getName();
        mesh->mMaterialIndex = 0;

        // Build vertices
        const auto& vertices = nativeMesh->getRealVertices();
        const auto nbVertex = (unsigned)vertices.size();

        mesh->mVertices = new aiVector3D[nbVertex];
        mesh->mNormals = new aiVector3D[nbVertex];
        mesh->mNumVertices = nbVertex;

        mesh->mTextureCoords[0] = new aiVector3D[nbVertex];
        mesh->mNumUVComponents[0] = nbVertex;

        for (Uint32 j = 0u; j < nbVertex; ++j)
        {
            const auto& vtx = vertices[j];

            mesh->mVertices[j] = aiVector3D(vtx.position.x, vtx.position.y, vtx.position.z);
            mesh->mNormals[j] = aiVector3D(vtx.normal.x, vtx.normal.y, vtx.normal.z);
            mesh->mTextureCoords[0][j] = aiVector3D(vtx.texCoord.x, vtx.texCoord.y, 0);
        }

        // Build faces
        const auto& indices = nativeMesh->getRealIndices();
        mesh->mNumFaces = (unsigned)indices.size() / PRIMITIVE_NB_VTX;
        mesh->mFaces = new aiFace[mesh->mNumFaces];

        for (Uint32 j = 0u; j < indices.size(); j += PRIMITIVE_NB_VTX)
        {
            aiFace &face = mesh->mFaces[j / PRIMITIVE_NB_VTX];
            face.mIndices = new unsigned int[PRIMITIVE_NB_VTX];
            face.mNumIndices = PRIMITIVE_NB_VTX;

            for (Uint32 k = 0; k < PRIMITIVE_NB_VTX; ++k)
            {
                face.mIndices[k] = indices[k + j];
            }
        }
    }

    std::lock_guard<std::mutex> lock(mtx);
    scene->mRootNode->addChildren(1, &node);
});


const auto objPath = m_serializationFullPath.string();
Assimp::Exporter exporter;
// HERE : SLOW  --------------------------------------------------------------------------------------------
exporter.Export(scene, "obj", objPath);
// ---------------------------------------------------------------------------------------------------------

delete scene;

回答

是否应该使用自定义格式?

如果你的应用仅需内部场景数据传递,无需兼容第三方工具,自定义二进制格式是最优选择——OBJ作为文本格式,本身的IO和解析开销远高于二进制格式,且Assimp的OBJ导出器为了兼容性做了大量冗余处理,速度自然受限。

如果必须兼容第三方工具,优先选择二进制3D格式(如glTF二进制版、FBX二进制版),这类格式的序列化效率远高于OBJ。

优化技巧参考

1. 针对Assimp的优化

  • 切换导出格式:放弃OBJ,改用glb(glTF二进制)或fbx(二进制)导出,Assimp对这类格式的导出效率远高于文本格式,且二进制IO本身速度更快。
  • 关闭冗余处理:创建Exporter时,通过属性设置关闭不必要的校验、转换逻辑,示例:
    exporter.SetPropertyBool(AI_CONFIG_EXPORT_FBX_EMIT_TRIANGLES, true); // 直接输出三角面,避免格式转换
    exporter.SetPropertyBool(AI_CONFIG_EXPORT_REMOVE_REDUNDANT_MATERIALS, true); // 移除冗余材质
    
  • 复用资源:若频繁执行序列化,避免每次创建新的aiScene和Exporter,复用已分配的内存结构减少分配/释放开销。

2. 代码层面的优化

  • 减少内存分配:当前代码大量使用new创建数组(如aiVector3D、aiFace),可改用内存池或std::vector管理内存,避免频繁堆操作。
  • 优化并行逻辑:创建aiMesh时,scene->mMeshes的访问因flatMeshIdx唯一无需加锁,仅保留添加节点时的锁;或批量添加节点,减少锁竞争次数。
  • 避免数据拷贝:当前代码将原生Mesh数据逐个拷贝到aiMesh,开销极大。若能让aiMesh直接指向原生数据内存(需保证数据生命周期),可大幅降低拷贝耗时。

3. 其他方向

  • 异步IO:将文件写入操作放到异步线程,避免阻塞主线程(仅隐藏耗时,不减少总耗时)。
  • 轻量压缩:若使用自定义二进制格式,加入LZ4等轻量压缩算法,减少IO量,压缩/解压开销远低于文本格式处理开销。
  • 合并网格:将场景中大量小网格合并为少量大网格,减少Assimp处理的节点和Mesh数量,降低序列化开销。

内容的提问来源于stack exchange,提问作者TheChamp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 15:12:04