3

我在 XNA 中有一个体素(我的世界风格)引擎,世界被分成块,当一个立方体被修改时,我通过扫描所有立方体并检查面是否可见来重新创建顶点缓冲区。这通常以很好的速度工作,但是当立方体以某种方式排列时(这样顶点缓冲区会大得多),它可以接管一个帧来生成新的块。在最糟糕的配置中(显示最多可能的顶点),它可能需要长达 100 毫秒。以下部分代码代表 100 次中的大约 80-90 毫秒,当块更新时它运行了 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]));
    }
}
4

2 回答 2

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 个顶点,这可能不切实际。可能需要减小块的大小。

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

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

  3. 我猜你注意到如果一个立方体被深深地隐藏了,我的意思是:只被隐藏的立方体包围,它会被你的算法显示出来。但要利用这一点,我只看到一个复杂的算法,比如 3D 绘画......嗯......

  4. 如果您知道发生的每个更改(只有一个块打开或关闭),您可能会通过仅更新顶点列表的一部分而不是每次都构建来大大加快速度。如果 One block 为 ON,请注意不需要重新构建所有块:只需添加新立方体的顶点,显示就可以了。但是顶点列表将包含您可能希望在某个时间清除的未显示顶点... :-) 如果一个块关闭,它将显示周围立方体的一些顶点,但仅在隐藏块周围的有限区域内. 所以有点像 vvnurmi 建议的那样,有一个 (Cube side) --> (Vertex start and end index) 字典会很方便。因此,您可以擦除给定的立方体。

于 2012-08-19T11:39:57.980 回答