首页 > 解决方案 > 使用 OpenGL 和 LibGDX 生成体素土地的低 FPS

问题描述

我遇到了一些与体素土地生成实施相关的性能问题。

我已将立方体分成 16x16 的块,并将这些块构建为 1 个单独的模型实例以进行渲染。为每个块创建的唯一面是暴露在空气中的面(意味着那一侧没有相邻的块)。

出于某种原因,在查看渲染的所有 36 个块时,我只能获得大约 26-32 FPS。如果我只渲染 4 个块,我会得到超过 300 FPS。考虑到渲染不多,我试图弄清楚我缺少什么优化以获得合理的 FPS。

这是我的世界渲染代码。

public void render(ModelBatch modelBatch, Player player) {
    modelBatch.begin(player.getCamera());
    for (Chunk chunk : chunks) {
        if (chunk.canSee(player.getCamera())) {
            chunk.render(modelBatch);
        }
    }
    modelBatch.end();
}

至于我的块渲染功能......

public void render(ModelBatch modelBatch) {
    modelBatch.render(chunkModelInstance);
}

我正在使用模型构建器并为每个块附加所有需要的面。然后使用它来构建模型并实例化模型实例以进行渲染。这是在 world.create() 期间完成的,而不是在渲染时完成的。

private void appendBlock(ModelBuilder modelBuilder, Block block) {
    Vector3 chunkLocation = block.getChunkLocation();

    Block topBlock = block.getAdjacentBlock(BlockFace.TOP);
    Block bottomBlock = block.getAdjacentBlock(BlockFace.BOTTOM);
    Block backBlock = block.getAdjacentBlock(BlockFace.BACK);
    Block frontBlock = block.getAdjacentBlock(BlockFace.FRONT);
    Block leftBlock = block.getAdjacentBlock(BlockFace.LEFT);
    Block rightBlock = block.getAdjacentBlock(BlockFace.RIGHT);

    if (frontBlock == null) {
        Material material = AssetWrapper.getInstance().getMaterial(block.getBlockMaterial().getFront());
        modelBuilder.part("front", GL20.GL_TRIANGLES, RENDER_ATTRIBUTES, material)
                .rect(chunkLocation.x + 0.5f,chunkLocation.y - 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y - 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y + 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y + 0.5f,chunkLocation.z - 0.5f,
                        0,0,-1);
    }
    if (backBlock == null) {
        Material material = AssetWrapper.getInstance().getMaterial(block.getBlockMaterial().getBack());
        modelBuilder.part("back", GL20.GL_TRIANGLES, RENDER_ATTRIBUTES, material)
                .rect(chunkLocation.x - 0.5f,chunkLocation.y - 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y - 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y + 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y + 0.5f,chunkLocation.z + 0.5f,
                        0,0,1);
    }
    if (bottomBlock == null) {
        Material material = AssetWrapper.getInstance().getMaterial(block.getBlockMaterial().getBottom());
        modelBuilder.part("bottom", GL20.GL_TRIANGLES, RENDER_ATTRIBUTES, material)
                .rect(chunkLocation.x - 0.5f,chunkLocation.y - 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y - 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y - 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y - 0.5f,chunkLocation.z + 0.5f,
                        0,-1,0);
    }
    if (topBlock == null) {
        Material material = AssetWrapper.getInstance().getMaterial(block.getBlockMaterial().getTop());
        modelBuilder.part("top", GL20.GL_TRIANGLES, RENDER_ATTRIBUTES, material)
                .rect(chunkLocation.x -0.5f,chunkLocation.y + 0.5f,chunkLocation.z -0.5f,
                        chunkLocation.x -0.5f,chunkLocation.y + 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y + 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y + 0.5f,chunkLocation.z -0.5f,
                        0,1,0);
    }
    if (leftBlock == null) {
        Material material = AssetWrapper.getInstance().getMaterial(block.getBlockMaterial().getLeft());
        modelBuilder.part("left", GL20.GL_TRIANGLES, RENDER_ATTRIBUTES, material)
                .rect(chunkLocation.x - 0.5f,chunkLocation.y - 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y - 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y + 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x - 0.5f,chunkLocation.y + 0.5f,chunkLocation.z - 0.5f,
                        -1,0,0);
    }
    if (rightBlock == null) {
        Material material = AssetWrapper.getInstance().getMaterial(block.getBlockMaterial().getRight());
        modelBuilder.part("right", GL20.GL_TRIANGLES, RENDER_ATTRIBUTES, material)
                .rect(chunkLocation.x + 0.5f,chunkLocation.y - 0.5f,chunkLocation.z + 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y - 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y + 0.5f,chunkLocation.z - 0.5f,
                        chunkLocation.x + 0.5f,chunkLocation.y + 0.5f,chunkLocation.z + 0.5f,
                        1,0,0);
    }
}

任何帮助将不胜感激。

标签: javaopengllibgdxlwjglvoxel

解决方案


您正在为每个块面创建一个 MeshPart 实例。它们中的每一个都可能绑定有不同的材料。这导致每个块面都有一个渲染调用 + 纹理绑定。

适当的体素渲染器的目标是在每个渲染过程中对整个块或块进行一次渲染调用。您可能会为每个块进行数千次渲染调用

不要为每个块创建多个 Mesh/MeshPart 实例。只保留一个并将面顶点附加到它。

您还应该放弃 Model/Mesh Builder,它没有针对您的用例进行优化。

看看 LibGDX 体素演示: https ://github.com/libgdx/libgdx/tree/master/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel

你应该采取这种方法。虽然它没有纹理,但如果你想添加纹理:创建一个包含所有块纹理的纹理图集,然后在顶点数据中引用它们的(纹理区域)uv 坐标。

纹理图集的替代方法是一种称为无绑定的技术,您可以一次将所有纹理绑定到多个单元,并从顶点数据中引用这些(它们的单元)。


推荐阅读