求助:我的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
相关产品推荐
相关产品推荐

