0

私は C++ でボクセル エンジンに取り組んでおり、チャンクを実装した後、チャンクを生成するのに非常にコストがかかることに気付きました。これはブロックを入れるという意味ではなく、チャンク メッシュを生成するという意味です。

ボクセルの配置と削除を除いて、チャンクが生成されると、ゲームはスムーズに実行されます。チャンクが変更されるたびに、メッシュが再構築されます。これは高価なプロセスです。1 つのチャンクを編集するのに約 0.36 秒かかるため、チャンクを編集すると約 0.36 秒間フリーズします。さらに、単一チャンクのこの 0.36 秒のスパイクのため、チャンク半径または 3 または 4 を超えるワールドをロードするには数分かかります。4 チャンクの場合、189 秒、(4*2)^3*0.36 (0.36 秒でそれぞれ 512 チャンク) かかります。

これは私のメッシュ生成コードです。チャンク内のすべてのブロックを繰り返し処理し、空気でない場合は立方体の頂点を追加し、空気でない場合は無視します。これは後で、私が計画したいくつかのものを含むより複雑な方法になります。これは、方法がすでに遅い場合は悪いことです。

void WorldRenderer::constructChunkMesh(Chunk* chunk)
{
    if (!chunk->isInitialized() || chunk->getNumBlocks() <= 0)
        return; //If the chunk isn't initialized, or is empty, don't construct anything for it.

    ChunkMesh mesh;

    //iterate over every block within the chunk.
    //CHUNK_SIZE has a value of 16. Each chunk is 16x16x16 blocks.
    for (int x = 0; x < CHUNK_SIZE; x++)
    {
        for (int y = 0; y < CHUNK_SIZE; y++)
        {
            for (int z = 0; z < CHUNK_SIZE; z++)
            {
                if (chunk->getBlock(x, y, z) != Blocks::BLOCK_TYPE_AIR) //if the block is solid, add vertices, otherwise, don't render it.
                {
                    //the 8 vertices for a cube. mesh.addVertex(...) returns the index.
                    int i0 = mesh.addVertex(Vertex(glm::vec3(0.0F, 0.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i1 = mesh.addVertex(Vertex(glm::vec3(1.0F, 0.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i2 = mesh.addVertex(Vertex(glm::vec3(0.0F, 1.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i3 = mesh.addVertex(Vertex(glm::vec3(1.0F, 1.0F, 1.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i4 = mesh.addVertex(Vertex(glm::vec3(0.0F, 0.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i5 = mesh.addVertex(Vertex(glm::vec3(1.0F, 0.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i6 = mesh.addVertex(Vertex(glm::vec3(0.0F, 1.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));
                    int i7 = mesh.addVertex(Vertex(glm::vec3(1.0F, 1.0F, 0.0F) + glm::vec3(x, y, z), glm::vec3(0.0F, 1.0F, 0.0F), glm::vec4(1.0F, 1.0F, 1.0F, 1.0F), glm::vec2(0.0F, 0.0F)));

                    //The xyz coord in the iteration in world-relative coordinates, instead of chunk-relative
                    int wx = (chunk->getPos().x * CHUNK_SIZE) + x;
                    int wy = (chunk->getPos().y * CHUNK_SIZE) + y;
                    int wz = (chunk->getPos().z * CHUNK_SIZE) + z;

                    //top       y+
                    if (World::getBlock(wx, wy + 1, wz) <= 0)
                    {
                        //if a block does not exist in the y+ direction to this one, add the top face.
                        mesh.addFace(i2, i3, i7);
                        mesh.addFace(i2, i7, i6);
                    }
                    //bottom    y-
                    if (World::getBlock(wx, wy - 1, wz) <= 0)
                    {
                        //if a block does not exist in the y- direction to this one, add the top face.
                        mesh.addFace(i0, i4, i1);
                        mesh.addFace(i1, i4, i5);
                    }
                    //front     z-
                    if (World::getBlock(wx, wy, wz - 1) <= 0)
                    {
                        //if a block does not exist in the z- direction to this one, add the top face.
                        mesh.addFace(i6, i7, i4);
                        mesh.addFace(i7, i5, i4);
                    }
                    //back      z+
                    if (World::getBlock(wx, wy, wz + 1) <= 0)
                    {
                        //if a block does not exist in the z+ direction to this one, add the top face.
                        mesh.addFace(i0, i1, i2);
                        mesh.addFace(i1, i3, i2);
                    }
                    //right     x+
                    if (World::getBlock(wx + 1, wy, wz) <= 0)
                    {
                        //if a block does not exist in the x+ direction to this one, add the top face.
                        mesh.addFace(i1, i7, i3);
                        mesh.addFace(i1, i5, i7);
                    }
                    //left      x-
                    if (World::getBlock(wx - 1, wy, wz) <= 0)
                    {
                        //if a block does not exist in the x- direction to this one, add the top face.
                        mesh.addFace(i2, i6, i4);
                        mesh.addFace(i0, i2, i4);
                    }
                }
            }
        }
    }


    //The rest of this is OpenGL code, and doesn't add any significant
    //performance drop. I have measured this.
    GeometryData gd = MeshHandler::compileGeometry(mesh.vertices.data(), mesh.indices.data(), mesh.vertices.size(), mesh.indices.size());

    RenderableChunk rc;
    rc.pos = chunk->getPos();
    auto a = std::find(chunks.begin(), chunks.end(), rc);
    int index = a - chunks.begin();

    if (a != chunks.end())
    {
        rc = chunks[index];
    }
    else
    {
        GLuint VAO;
        GLuint* VBOs = new GLuint[2];

        //1527864 bytes maximum per chunk (1.5MB)

        glGenVertexArrays(1, &VAO);
        glBindVertexArray(VAO);

        glGenBuffers(2, VBOs);
        glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 8 * MAX_BLOCKS, nullptr, GL_DYNAMIC_DRAW);

        glVertexAttribPointer(ATTRIB_VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, position)));
        glEnableVertexAttribArray(ATTRIB_VERTEX_ARRAY);
        glVertexAttribPointer(ATTRIB_NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, normal)));
        glEnableVertexAttribArray(ATTRIB_NORMAL_ARRAY);
        glVertexAttribPointer(ATTRIB_COLOUR_ARRAY, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, colour)));
        glEnableVertexAttribArray(ATTRIB_COLOUR_ARRAY);
        glVertexAttribPointer(ATTRIB_TEXTURE_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, texture)));
        glEnableVertexAttribArray(ATTRIB_TEXTURE_ARRAY);

        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOs[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * 36 * MAX_BLOCKS, nullptr, GL_DYNAMIC_DRAW);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        glBindVertexArray(0);

        rc.VAO = VAO;
        rc.VBOs = VBOs;
    }

    rc.numIndices = gd.numIndices;

    glBindVertexArray(rc.VAO);

    glBindBuffer(GL_ARRAY_BUFFER, rc.VBOs[0]);
    glBufferSubData(GL_ARRAY_BUFFER, 0, gd.vboSize(), gd.vertices);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rc.VBOs[1]);
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, gd.iboSize(), gd.indices);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

    if (index >= 0 && index < chunks.size())
    {
        chunks[index] = rc;
    }
    else
    {
        chunks.push_back(rc);
    }
}

そして、ChunkMesh使用されている構造体。問題は次のとおりです。

struct ChunkMesh
{
    std::vector<Vertex> vertices;
    std::vector<GLushort> indices;

    int addVertex(Vertex v)
    {
        //add a vertex to the mesh, and return its index in the list.
        vertices.push_back(v);
        return vertices.size() - 1;
    }

    void addFace(int v0, int v1, int v2)
    {
        //construct a face with 3 vertices.
        indices.push_back(v0);
        indices.push_back(v1);
        indices.push_back(v2);
    }
};

問題は、chunkMesh 構造体にあり、push_backs が使用されていると思います。数百回のstd::vectorpush_back では非常に遅いですが、代替手段が見つかりません。ベクトルを何に置き換えることができますか?

チャンクを完全に間違ってレンダリングしようとしていますか? この機能を最適化するにはどうすればよいですか?

どんな助けでも大歓迎です。

ありがとう。

編集: ベクトルを予約しようとしましたが、混乱してパフォーマンスに影響はありませんでした。0.36秒のままです。

ChunkMesh次のように、ブロックの数を取得するコンストラクターを追加しました。

ChunkMesh(int numBlocks)
{
    vertices.reserve(numBlocks * 8); //8 vertices per cube
    indices.reserve(numBlocks * 36); //36 indices per cube
}
4

1 に答える 1

1

チャンクの表面にない頂点が必要かどうかを評価することをお勧めします。

そうでない場合は、それらを ChunkMesh に追加する必要はありません。これにより、頂点の数と push_back 呼び出しが顕著に減少します。

于 2016-07-14T15:35:16.403 に答える