有什么方法可以加速这个函数吗?

3
我在 XNA 中有一个体素引擎(类似 Minecraft),世界被分成块,当方块修改时,我通过扫描所有方块并检查面是否可见来重新创建顶点缓冲区。这通常速度很快,但当方块排列方式特定时(使顶点缓冲区更大),生成新块可能需要超过一帧时间。 在最糟糕的配置中(显示最多顶点),可能需要100毫秒。 以下代码部分代表了100毫秒中的80-90%,当块更新时,在最坏的情况下它创建32,768个立方体,在其他所有运行时它都不会创建任何顶点。 我使用 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]));
    }
}

1
首先,您可以获取GlobalWorld.Blocks[id]一次并将其存储在本地变量中(除非每次调用GlobalWorld.Blocks[id]时返回不同的值)。对于任何其他重复访问的类属性,应用相同的哲学。此外,这非常具体,因此不适合在SO上发布。请尝试http://gamedev.stackexchange.com。 - Igby Largeman
为什么你的 Byte4 使用浮点数? - Daniel
因为我从Vector4更改了它们,但忘记更改它们,感谢您提醒我。为什么从GlobalWorld.Blocks[id]获取变量会导致减速?速度不应该是相同的吗? - Levi H
1
@Levi 需要重新评估GlobalWorld上的Blocks getter,并重新检索Blocks的“id”元素。最好只做一次并重复使用该值。 - Chris Sinclair
1
谢谢,但是没有明显的效果。我想我必须减少块大小并牺牲更高的帧速率。 - Levi H
看起来很蠢,但预先计算各种“+1”可能会有所帮助。并且可以提取您(可能)冗余的GlobalWorld.Blocks查找。 - bluevector
2个回答

1
  1. 在“典型”配置中,隐藏顶点/显示顶点比率是什么?因为,由于图形卡不会显示隐藏的顶点,所以删除边缘可能需要更长时间,而不仅仅是将它们扔到图形卡上。

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

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

  4. 如果您知道每次发生的更改(只有一个块打开或关闭),则可以通过仅更新部分顶点列表而不是每次构建来大大加快速度。 如果一个块打开,注意重新构建并不是必需的:只需添加新立方体的顶点,显示就可以了。但是,顶点列表将包含您可能希望在某个时候清除的未显示顶点... :-) 如果一个块关闭,它将显示周围立方体的一些顶点,但仅在隐藏块周围的有限区域内。 因此,就像vvnurmi建议的那样,拥有一个(立方体边)->(顶点起始和结束索引)字典会很方便。这样您就可以删除给定的立方体。


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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接