3

私は XNA にボクセル (Minecraft スタイル) エンジンを持っています。世界はチャンクに分割され、キューブが変更されると、すべてのキューブをスキャンして面が表示されているかどうかを確認することで、頂点バッファーを再作成します。これは通常、かなりの速度で機能しますが、立方体が特定の方法で配置されている場合 (頂点バッファーが非常に大きくなるように)、新しいチャンクを生成するためにフレームを引き継ぐ可能性があります。考えられる最悪の構成 (可能な限り多くの頂点が表示されている) では、最大 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

このコードの遅さの明確な理由はわかりません。コードを少し変更しても必要なスピードアップが得られないようであれば、別の実装を試してみます。

私の理解が正しければ、1 つのブロックが変更されると、64x64x32 (= 131072) ブロックのチャンク全体の頂点バッファーを再構築します。チャンク内のブロックが一度にわずかしか変更されないと仮定すると、所定の場所にあるすべての立方体のすべての側面を含む 1 つの永続的な頂点バッファーを持つ方がはるかに高速になる可能性があります。1 つの立方体面のステータスが変更された場合、頂点バッファー全体を最初から作成するのではなく、頂点バッファー内の 4 つの値を変更するだけで済みます。

たとえば、立方体の上面の 4 つの頂点を (5, 6, 7) に、 から始まる 4 つの連続する頂点バッファー インデックスにすることができます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));
}

(ブロックが削除されたときに) 1 つの面を削除するには、4 つの頂点を同じ値に設定しますnew VertexPositionNormalSmall(Vector4.Zero, DummyProperties)。頂点が同じ位置を共有する三角形は画面に表示されないことに注意してください。

面を追加したり、そのプロパティを変更したりする必要がある場合は、立方体と面の位置によって決定されるインデックスでのみ、以前と同じように行います。

もちろん、この実装には、より大きな頂点バッファーが必要です。チャンクのサイズが 64x64x32 キューブの場合、頂点バッファーは 64*64*64*6*4 = 3145728 頂点の長さが必要になり、実用的ではない可能性があります。チャンクのサイズを小さくする必要がある場合があります。

于 2012-08-19T09:55:21.417 に答える
1
  1. 「典型的な」構成での隠し頂点 // 表示頂点の比率は? いずれにせよ、グラフィック カードは非表示の頂点を表示しないため、単にグラフィック カードにエッジを投げるよりも、エッジを削除する方が時間がかかる場合があります。

  2. 同じ種類のアイデアで、キューブ レベルでのみ処理することは興味深いかもしれません。キューブが完全に他のキューブ内にある場合は、頂点を追加しないでください。そうでない場合は、すべてのキューブ頂点をチェックせずに追加します。ここでは、非表示/表示の頂点比率に依存します。

  3. キューブが深く隠されている場合、つまり、隠されたキューブのみに囲まれている場合、アルゴリズムによって表示されることに気付いたと思います。しかし、それを利用するには、3Dペイントのような複雑なアルゴリズムしか見ていません...うーん....

  4. 発生する各変更 (1 つのブロックのみがオンまたはオフになる) がわかっている場合は、毎回すべてを構築するのではなく、頂点リストの一部のみを更新することで処理速度が大幅に向上する可能性があります。1 つのブロックがオンになった場合、すべてを再度構築する必要はないことに注意してください。新しい立方体の頂点を追加するだけで、表示は問題ありません。ただし、頂点リストには、いつかクリアしたい非表示の頂点が含まれます... :-) 1 つのブロックがオフになると、周囲の立方体の頂点の一部が表示されますが、非表示のブロックの周囲の限定されたエリアのみに表示されます. したがって、vvnurmi が示唆するように、(キューブ側) --> (頂点の開始インデックスと終了インデックス) ディクショナリがあると便利です。したがって、特定のキューブを消去できます。

于 2012-08-19T11:39:57.980 に答える