[英]why is my marching cubes algorithm so slow?
我已经在 unity/c# 中实现了行进立方体(尽管你不需要知道 unity 来帮助我)而且我不能停止感觉我在我的代码中犯了一个大错误,因为它太慢了。 我已经在一个单独的线程上运行它,但它只需要很长时间才能完成。 请帮我优化我的代码。
private void _UpdateChunk()
{
lock (this)
{
// clear the tri, vert and uv lists
ClearMeshData();
// Loop through each "cube" in the terrain.
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < width; z++)
{
// Create an array of floats representing each corner of a cube and get the value from our terrainMap.
float[] cube = new float[8];
float[] strengths = new float[8];
for (int i = 0; i < 8; i++)
{
Vector3Int corner = new Vector3Int(x, y, z) + gamedata.CornerTable[i];
cube[i] = terrainMap[corner.x, corner.y, corner.z].BlockType;
strengths[i] = terrainMap[corner.x, corner.y, corner.z].Strength;
}
// Pass the value into the MarchCube function.
MarchCube(new Vector3(x, y, z), cube, strengths);
}
}
}
}
}
void MarchCube(Vector3 position, float[] cube, float[] strengths)
{
// Get the configuration index of this cube.
int configIndex = GetCubeConfiguration(cube);
// If the configuration of this cube is 0 or 255 (completely inside the terrain or completely outside of it) we don't need to do anything.
if (configIndex == 0 || configIndex == 255)
return;
// Loop through the triangles. There are never more than 5 triangles to a cube and only three vertices to a triangle.
int edgeIndex = 0;
Vector3 vert1 = new Vector3();
Vector3 vert2 = new Vector3();
float vert1sample = 0;
float vert2sample = 0;
float lerp = 0;
int indice = 0;
for (int i = 0; i < 5; i++)
{
for (int p = 0; p < 3; p++)
{
// Get the current indice. We increment triangleIndex through each loop.
indice = gamedata.TriangleTable[configIndex, edgeIndex];
// If the current edgeIndex is -1, there are no more indices and we can exit the function.
if (indice == -1)
return;
// Get the vertices for the start and end of this edge.
vert1 = position + gamedata.EdgeTable[indice, 0];
vert2 = position + gamedata.EdgeTable[indice, 1];
vert1sample = strengths[gamedata.EdgeIndexTable[indice, 0]];
vert2sample = strengths[gamedata.EdgeIndexTable[indice, 1]];
// Get the midpoint of this edge.
lerp = Mathf.Abs(vert1sample) / (Mathf.Abs(vert2sample) + Mathf.Abs(vert1sample));
Vector3 vertPosition = Vector3.Lerp(vert1, vert2, lerp);
// Add to our vertices and triangles list and incremement the edgeIndex.
vertices.Add(vertPosition);
triangles.Add(vertices.Count - 1);
if (getChunkVoxel(vert1 + chunkPosition) != 0)
{
uvs.Add(new Vector2(getChunkVoxel(vert1 + chunkPosition) - 1, 0));
}
else
{
uvs.Add(new Vector2(getChunkVoxel(vert2 + chunkPosition) - 1, getChunkVoxel(vert2 + chunkPosition) - 1));
}
edgeIndex++;
}
}
}
int GetCubeConfiguration(float[] cube)
{
// Starting with a configuration of zero, loop through each point in the cube and check if it is below the terrain surface.
int configurationIndex = 0;
for (int i = 0; i < 8; i++)
{
// If it is, use bit-magic to the set the corresponding bit to 1. So if only the 3rd point in the cube was below
// the surface, the bit would look like 00100000, which represents the integer value 32.
if (cube[i] < terrainSurface)
configurationIndex |= 1 << i;
}
return configurationIndex;
}
看来这是减慢我的游戏速度的部分,我们将不胜感激
我已经通过将 terrainpoint 从 class 更改为结构来使其更快,但它仍然很慢。
它缓慢的一个主要原因是循环中有很多分配给垃圾收集器带来了很大压力。 目前在 _UpdateChunk 的地形中每个“立方体”有 11 个分配,在_UpdateChunk
中最多有 17 MarchCube
(如果像position + gamedata.EdgeTable[indice, 0]
这样的表达式分配一个新的向量,可能会更多)。 这是不合理的。 许多分配是不需要的。 例如,可以在_UpdateChunk
的开头为所有立方体预分配一次cube
和strengths
。 您不需要在表达式中分配向量来计算corner
:您可以手动单独计算组件(或者您可以预先分配向量并在需要时重置其组件)。 同样的事情适用于new Vector3(x, y, z)
可以在循环中预先分配和设置。 这样的算法是计算密集型的,因此您应该避免任何开销,如虚拟方法调用和分配/GC——只应保留低级 arrays 访问和数学运算。
请注意,可以优化某些计算。 例如GetCubeConfiguration
可以修改为无分支。 Mathf.Abs(vert1sample)
可以预先计算,这样就不会计算两次(尽管编译器可能已经这样做了)。 我也想知道像vertices.Add
这样的表达式是否有效,但这取决于此处未提供的容器类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.