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

求助:我的Marching Cubes算法运行极慢,求优化方案

Marching Cubes 性能优化建议

以下是针对你的核心代码的具体优化方向和实现方案:


1. 消除频繁内存分配,复用临时变量

每次遍历立方体时创建float[8]数组、Vector3Int等临时对象会产生大量GC垃圾,直接拖慢运行速度。把这些临时对象改成类级别成员复用:

// 类级别成员变量,提前初始化一次
private float[] _cubeCache = new float[8];
private float[] _strengthsCache = new float[8];
private Vector3Int _cornerTemp = new Vector3Int();
private Vector3 _positionTemp = new Vector3();

private void _UpdateChunk()
{
    lock (this)
    {
        ClearMeshData();

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                for (int z = 0; z < width; z++)
                {
                    // 复用缓存数组,避免每次new
                    for (int i = 0; i < 8; i++)
                    {
                        _cornerTemp.x = x + gamedata.CornerTable[i].x;
                        _cornerTemp.y = y + gamedata.CornerTable[i].y;
                        _cornerTemp.z = z + gamedata.CornerTable[i].z;
                        var terrainPoint = terrainMap[_cornerTemp.x, _cornerTemp.y, _cornerTemp.z];
                        _cubeCache[i] = terrainPoint.BlockType;
                        _strengthsCache[i] = terrainPoint.Strength;
                    }

                    _positionTemp.Set(x, y, z);
                    MarchCube(_positionTemp, _cubeCache, _strengthsCache);
                }
            }
        }
    }
}

2. 预分配集合容量,避免扩容拷贝

vertices、triangles、uvs这些List每次扩容都会触发内存拷贝,提前预估最大容量可以大幅减少开销:

private void ClearMeshData()
{
    vertices.Clear();
    triangles.Clear();
    uvs.Clear();
    
    // 按每个立方体最多生成15个顶点(5个三角形×3)预估容量
    int maxElementCount = width * height * width * 15;
    if (vertices.Capacity < maxElementCount) vertices.Capacity = maxElementCount;
    if (triangles.Capacity < maxElementCount) triangles.Capacity = maxElementCount;
    if (uvs.Capacity < maxElementCount) uvs.Capacity = maxElementCount;
}

3. 减少重复计算与冗余方法调用

  • 把GetCubeConfiguration的逻辑内联到循环中,省去方法调用和数组传递的开销:
// 在_UpdateChunk的三层循环内,替代原有的数组填充和MarchCube调用
int configIndex = 0;
for (int i = 0; i < 8; i++)
{
    _cornerTemp.x = x + gamedata.CornerTable[i].x;
    _cornerTemp.y = y + gamedata.CornerTable[i].y;
    _cornerTemp.z = z + gamedata.CornerTable[i].z;
    var terrainPoint = terrainMap[_cornerTemp.x, _cornerTemp.y, _cornerTemp.z];
    _cubeCache[i] = terrainPoint.BlockType;
    _strengthsCache[i] = terrainPoint.Strength;

    if (_cubeCache[i] < terrainSurface)
        configIndex |= 1 << i;
}

// 提前判断,直接跳过无需处理的立方体
if (configIndex == 0 || configIndex == 255)
    continue;

_positionTemp.Set(x, y, z);
// 修改MarchCube方法,直接传入预计算的configIndex,省去内部计算
MarchCube(_positionTemp, _strengthsCache, configIndex);
  • 缓存getChunkVoxel的结果,避免重复调用:
// 在MarchCube方法内的UV生成逻辑处
var vert1World = vert1 + chunkPosition;
int voxelVal1 = getChunkVoxel(vert1World);
if (voxelVal1 != 0)
{
    uvs.Add(new Vector2(voxelVal1 - 1, 0));
}
else
{
    var vert2World = vert2 + chunkPosition;
    int voxelVal2 = getChunkVoxel(vert2World);
    uvs.Add(new Vector2(voxelVal2 - 1, voxelVal2 - 1));
}

4. 优化数组索引访问

提前获取TriangleTable的整行数据,减少二维数组索引的开销:

void MarchCube(Vector3 position, float[] strengths, int configIndex)
{
    int[] triangleRow = gamedata.TriangleTable[configIndex];
    int edgeIndex = 0;
    Vector3 vert1 = new Vector3();
    Vector3 vert2 = new Vector3();
    
    for (int i = 0; i < 5; i++)
    {
        for (int p = 0; p < 3; p++)
        {
            int indice = triangleRow[edgeIndex];
            if (indice == -1)
                return;
                
            vert1 = position + gamedata.EdgeTable[indice, 0];
            vert2 = position + gamedata.EdgeTable[indice, 1];
            float vert1sample = strengths[gamedata.EdgeIndexTable[indice, 0]];
            float vert2sample = strengths[gamedata.EdgeIndexTable[indice, 1]];
            
            // 优化插值计算,避免重复取绝对值
            float abs1 = Mathf.Abs(vert1sample);
            float abs2 = Mathf.Abs(vert2sample);
            float denominator = abs1 + abs2;
            float lerp = denominator == 0 ? 0.5f : abs1 / denominator;
            
            Vector3 vertPosition = Vector3.Lerp(vert1, vert2, lerp);
            
            vertices.Add(vertPosition);
            triangles.Add(vertices.Count - 1);
            
            // ... 缓存后的UV生成逻辑
            edgeIndex++;
        }
    }
}

5. 缩小锁的范围

原代码中lock(this)包裹了整个计算过程,会阻塞其他线程。可以先在无锁环境下用临时集合计算数据,最后再锁定更新共享集合:

private void _UpdateChunk()
{
    // 用临时集合存储计算结果,避免锁占用整个计算过程
    List<Vector3> tempVertices = new List<Vector3>(width * height * width * 15);
    List<int> tempTriangles = new List<int>(width * height * width * 15);
    List<Vector2> tempUvs = new List<Vector2>(width * height * width * 15);

    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            for (int z = 0; z < width; z++)
            {
                // ... 所有计算逻辑,把结果添加到temp集合中
            }
        }
    }

    // 只在更新共享集合时加锁
    lock (this)
    {
        ClearMeshData();
        vertices.AddRange(tempVertices);
        triangles.AddRange(tempTriangles);
        uvs.AddRange(tempUvs);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:35:17