使用 OpenGL 和 LibGDX 在体素土地生成中的低 FPS
Low FPS in Voxel land generation with OpenGL & LibGDX
我在实施体素土地生成方面遇到了一些性能问题。
我已将立方体分成 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);
}
我正在使用模型构建器并为每个块附加所有需要的面。然后使用它来构建模型并实例化 ModelInstance 以进行渲染。这是在 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);
}
}
如有任何帮助,我们将不胜感激。
您正在为每个块面创建一个 MeshPart 实例。他们每个人都可能有不同的 material 绑定到它。这导致每个方块面都有一个渲染调用 + 纹理绑定。
正确的体素渲染器的目标是每个渲染通道的每个块或小块有 一个渲染调用。您可能会对每个块进行数千次渲染调用。
不要为每个块创建多个 Mesh/MeshPart 实例。只保留一个并向其附加面顶点。
您还应该放弃 Model/Mesh Builder,它并未针对您的用例进行优化。
看看LibGDX体素演示:
https://github.com/libgdx/libgdx/tree/master/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel
你应该采用这种方法。虽然它没有纹理,但如果你想添加纹理:创建一个包含所有块纹理的纹理图集,然后在顶点数据中引用它们的(纹理区域)uv 坐标。
纹理图集的替代方法是一种称为无绑定的技术,您可以将所有纹理一次绑定到多个单元并从顶点数据中引用这些单元(它们的单元)。
我在实施体素土地生成方面遇到了一些性能问题。
我已将立方体分成 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);
}
我正在使用模型构建器并为每个块附加所有需要的面。然后使用它来构建模型并实例化 ModelInstance 以进行渲染。这是在 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);
}
}
如有任何帮助,我们将不胜感激。
您正在为每个块面创建一个 MeshPart 实例。他们每个人都可能有不同的 material 绑定到它。这导致每个方块面都有一个渲染调用 + 纹理绑定。
正确的体素渲染器的目标是每个渲染通道的每个块或小块有 一个渲染调用。您可能会对每个块进行数千次渲染调用。
不要为每个块创建多个 Mesh/MeshPart 实例。只保留一个并向其附加面顶点。
您还应该放弃 Model/Mesh Builder,它并未针对您的用例进行优化。
看看LibGDX体素演示: https://github.com/libgdx/libgdx/tree/master/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel
你应该采用这种方法。虽然它没有纹理,但如果你想添加纹理:创建一个包含所有块纹理的纹理图集,然后在顶点数据中引用它们的(纹理区域)uv 坐标。
纹理图集的替代方法是一种称为无绑定的技术,您可以将所有纹理一次绑定到多个单元并从顶点数据中引用这些单元(它们的单元)。