简体   繁体   English

为什么我的行进立方体算法这么慢?

[英]why is my marching cubes algorithm so slow?

i have somewhat implemented marching cubes in unity/c# (you dont need to know unity to help me though) and i cant stop feeling like i have made a big mistake in my code because it is so slow.我已经在 unity/c# 中实现了行进立方体(尽管你不需要知道 unity 来帮助我)而且我不能停止感觉我在我的代码中犯了一个大错误,因为它太慢了。 i am already running it on a separate thread but it just takes ages to complete.我已经在一个单独的线程上运行它,但它只需要很长时间才能完成。 please help me optimize my code.请帮我优化我的代码。



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;

}


it appears that this is the part that slows my game down, help would be appreciated看来这是减慢我的游戏速度的部分,我们将不胜感激

i already made it faster by changing terrainpoint from a class to a struct but it is still very slow.我已经通过将 terrainpoint 从 class 更改为结构来使其更快,但它仍然很慢。

One main reason it is slow is that there is a lot of allocations in the loop putting a lot of pressure on the garbadge collector .它缓慢的一个主要原因是循环中有很多分配垃圾收集器带来了很大压力 There is currently 11 allocation per "cube" in the terrain in _UpdateChunk and up to 17 in MarchCube (possibly even more if the expressions like position + gamedata.EdgeTable[indice, 0] allocates a new vector).目前在 _UpdateChunk 的地形中每个“立方体”有 11 个分配,在_UpdateChunk中最多有 17 MarchCube (如果像position + gamedata.EdgeTable[indice, 0]这样的表达式分配一个新的向量,可能会更多)。 This is not reasonable.这是不合理的。 Many allocation are not needed .许多分配是不需要的 For example cube and strengths can be preallocated once for all the cubes in the beginning of _UpdateChunk .例如,可以在_UpdateChunk的开头为所有立方体预分配一次cubestrengths You do not need to allocate the vector in the expression to compute corner : you can just compute the components separately manually (or you can possibly preallocate the vector and reset its component when needed).您不需要在表达式中分配向量来计算corner :您可以手动单独计算组件(或者您可以预先分配向量并在需要时重置其组件)。 The same thing applies for the new Vector3(x, y, z) can can be preallocated and set in the loop.同样的事情适用于new Vector3(x, y, z)可以在循环中预先分配和设置。 Such an algorithm is computationally intensive so you should get away any overhead like virtual method calls and allocations/GC -- only low-level arrays accesses and mathematical operations should remains.这样的算法是计算密集型的,因此您应该避免任何开销,如虚拟方法调用和分配/GC——只应保留低级 arrays 访问和数学运算。

Note that some computations can be optimized.请注意,可以优化某些计算。 For example GetCubeConfiguration can be modified so to be branchless.例如GetCubeConfiguration可以修改为无分支。 Mathf.Abs(vert1sample) can be precomputed so not to compute it twice (though the compiler may already do that). Mathf.Abs(vert1sample)可以预先计算,这样就不会计算两次(尽管编译器可能已经这样做了)。 I am also wondering if the expression like vertices.Add are efficient but this is dependent of the type of container which is not provided here.我也想知道像vertices.Add这样的表达式是否有效,但这取决于此处未提供的容器类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM