无论如何要加快这个功能?

时间:2012-05-10 17:03:28

标签: c# xna

我在XNA中有一个体素(我的世界风格)引擎,世界被分成块,当修改立方体时,我通过扫描所有立方体并检查面是否可见来重新创建顶点缓冲区。这通常以很好的速度工作,但是当以某些方式排列立方体(使得顶点缓冲区将要大得多)时,它可以接管一个帧来生成新的块。在最糟糕的配置中(显示最可能的顶点),它可能需要长达100毫秒。代码的以下部分代表100的大约80-90ms,当块更新时它运行131,072次,在最坏的可能配置上它创建32,768个立方体,而在所有其他运行中它创建0个顶点。

我也在使用i5 2500k,因此在旧系统上可能会非常糟糕。

我无论如何都无法提高速度,我对编程很陌生,所以我想我会在这里发布一些建议吗?感谢。

public void GenerateCubeGeometryAtPosition(int x, int y, int z, byte id)
{
    //We check if there's a cube in the six directions around the cube
    //if there's no cube ot the cube is transparent we add the vertices to the vertex list
    //if the cube is on the outside of the chunk we check the cube in the chunk next to it assuming it's loaded

    //if we have to build part of the cube we make a new vertex
    //first 3 bytes in the vertex are the position relative to the chunk
    //4th byte is for normals, we check it in the shader to figure out the normal
    //the next 2 bytes in the properties are for the texture positons on the texture atlas
    //last 2 bytes are for other properties of the vertex like light/shade etc

    //Check up and down
    if (y > YSize - 2)
    {
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, 1 + z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, 1 + z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[3]));
    }
    else if (Blocks[x, y + 1, z] == 0 || GlobalWorld.Blocks[Blocks[x, y + 1, z]].Transparent)
    {
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, 1 + z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, 1 + z, 0f), GlobalWorld.Blocks[id].VertexPropertiesTop[3]));

    }
    if (y != 0 && (Blocks[x, y - 1, z] == 0 || GlobalWorld.Blocks[Blocks[x, y - 1, z]].Transparent))
    {
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, z, 1f), GlobalWorld.Blocks[id].VertexPropertiesBottom[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, 1 + z, 1f), GlobalWorld.Blocks[id].VertexPropertiesBottom[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, z, 1f), GlobalWorld.Blocks[id].VertexPropertiesBottom[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, 1 + z, 1f), GlobalWorld.Blocks[id].VertexPropertiesBottom[3]));
    }

    //check Right and Left of the cube and the adjacent chunk at the edges
    if (x == 0)
    {
        if (this.RightChunk != -1 && (GlobalWorld.LoadedChunks[this.RightChunk].Blocks[XSize - 1, y, z] == 0 || GlobalWorld.Blocks[GlobalWorld.LoadedChunks[this.RightChunk].Blocks[XSize - 1, y, z]].Transparent))
        {
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[0]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[1]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, 1 + z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[2]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, 1 + z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[3]));
        }
    }
    else if (Blocks[x - 1, y, z] == 0 || GlobalWorld.Blocks[Blocks[x - 1, y, z]].Transparent)
    {
        //right
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, 1 + z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, 1 + z, 3f), GlobalWorld.Blocks[id].VertexPropertiesRight[3]));
    }
    if (x > XSize - 2)
    {
        if (this.LeftChunk != -1 && (GlobalWorld.LoadedChunks[this.LeftChunk].Blocks[0, y, z] == 0 || GlobalWorld.Blocks[GlobalWorld.LoadedChunks[this.LeftChunk].Blocks[0, y, z]].Transparent))
        {
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[0]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, 1 + z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[1]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, 1 + y, z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[2]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, 1 + y, 1 + z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[3]));
        }
    }
    else if (Blocks[x + 1, y, z] == 0 || GlobalWorld.Blocks[Blocks[x + 1, y, z]].Transparent)
    {
        //left
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, 1 + z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, 1 + y, z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, 1 + y, 1 + z, 2f), GlobalWorld.Blocks[id].VertexPropertiesLeft[3]));
    }

    //check Back and Front of the cube and the adjacent chunk at the edges
    if (z == 0)
    {
        if (this.BackChunk != -1 && (GlobalWorld.LoadedChunks[this.BackChunk].Blocks[x, y, ZSize - 1] == 0 || GlobalWorld.Blocks[GlobalWorld.LoadedChunks[this.BackChunk].Blocks[x, y, ZSize - 1]].Transparent))
        {
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[0]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[1]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[2]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[3]));
        }
    }
    else if (Blocks[x, y, z - 1] == 0 || GlobalWorld.Blocks[Blocks[x, y, z - 1]].Transparent)
    {
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, z, 5f), GlobalWorld.Blocks[id].VertexPropertiesBack[3]));
    }
    if (z > ZSize - 2)
    {
        if (this.ForwardChunk != -1 && (GlobalWorld.LoadedChunks[this.ForwardChunk].Blocks[x, y, 0] == 0 || GlobalWorld.Blocks[GlobalWorld.LoadedChunks[this.ForwardChunk].Blocks[x, y, 0]].Transparent))
        {
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[0]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[1]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[2]));
            ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[3]));
        }
    }
    else if (Blocks[x, y, z + 1] == 0 || GlobalWorld.Blocks[Blocks[x, y, z + 1]].Transparent)
    {
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[0]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(x, y + 1, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[1]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[2]));
        ChunkVertices.Add(new VertexPositionNormalSmall(new Byte4(1 + x, y + 1, 1 + z, 4f), GlobalWorld.Blocks[id].VertexPropertiesFront[3]));
    }
}

2 个答案:

答案 0 :(得分:1)

我认为此代码中没有明显的缓慢原因。如果小代码更改似乎没有给你必要的加速,我会尝试不同的实现。

如果我理解正确,当一个块改变时,你会为整个64x64x32(= 131072)个块重建顶点缓冲区。假设一个块中的块一次只改变几个,那么在一个预定的位置有一个包含所有立方体的所有边的持久顶点缓冲区可能要快得多。当一个立方体面的状态发生变化时,您只需要更改顶点缓冲区中的四个值,而不是从头开始创建整个顶点缓冲区。

例如,您可以将多维数据集顶面的四个顶点放在(5,6,7)四个连续的顶点缓冲区索引处,从GetCubeFaceStartIndex(5, 6, 7, CubeFaceType.Top)开始。

enum CubeFaceType { Top, Bottom, Left, Right, Front, Back }

int GetCubeFaceStartIndex(int x, int y, int z, CubeFaceType face)
{
    return 4 * ((int)cubeFace + 6 * (x + CHUNK_WIDTH * (y + CHUNK_HEIGHT * z));
}

要删除一个面(删除块时),可以将四个顶点设置为相同的值,例如new VertexPositionNormalSmall(Vector4.Zero, DummyProperties)。请注意,屏幕上看不到顶点共享相同位置的三角形。

如果您需要添加面部或更改其属性,您可以执行之前所做的操作,只能直接在由立方体和面部位置确定的索引处。

此实现当然需要更大的顶点缓冲区。如果您的块大小为64x64x32多维数据集,则顶点缓冲区需要为64 * 64 * 64 * 6 * 4 = 3145728个顶点,这可能不实用。减少块的大小可能是必要的。

答案 1 :(得分:1)

  1. 在“典型”配置中,隐藏顶点//显示顶点比率是多少?因为,由于图形卡无论如何都不会显示隐藏的顶点,因此删除边缘可能需要更多时间,而不是将它们扔到图形卡上。

  2. 在同样的想法中,也许仅在多维数据集级别处理可能会很有趣:如果一个立方体完全位于其他立方体内,则不添加顶点,如果不添加所有立方体顶点没检查。这取决于隐藏/显示的顶点比率。

  3. 我猜你注意到如果一个立方体被深深隐藏,我的意思是:只有隐藏的立方体包围,它将由你的算法显示。但是为了利用这一点,我只看到一个复杂的算法,如3D绘画... mmm ....

  4. 如果您知道每次发生的更改(只有一个块开启或关闭),您可以通过仅更新顶点列表的一部分而不是每次都建立所有内容来大大加快速度。 如果One块为ON,请注意再次构建all不是必需的:只需添加新多维数据集的顶点,显示就可以了。但顶点列表将包含您可能希望在某个时间清除的非显示顶点... :-) 如果一个块关闭,它将显示周围立方体的一些顶点,但仅限于隐藏块周围的有限区域。 所以有点像vvnurmi建议,有一个(Cube方面) - >会很方便。 (顶点开始和结束索引)字典。所以你可以删除给定的立方体。