OpenGL ES 2.0如何提高体素世界的性能?

时间:2014-08-04 10:07:54

标签: android performance opengl-es opengl-es-2.0

我使用OpenGL ES 2.0创建了一个体素世界,使用VBO存储基本立方体并为每个立方体使用不同的位置矩阵。当有500-600个立方体被渲染时,我能够在我的Galaxy S3上获得30fps,但是超过1500个立方体的任何东西都不能以比8 fps更快的速度运行。这是不可接受的,因为体素世界应该能够以稳定的30fps处理超过5,000个体素。我在手机上玩过其他手机游戏,运行速度很好,一次渲染超过5000块。什么样的技术最适合获得良好的表现?

以下是我更详细的设置: 有一个VBO包含基本多维数据集的顶点信息。 每个块都有自己的矩阵,该矩阵被转换为块在世界空间中的位置(该矩阵在创建块时仅计算一次)。该块调用glDrawArrays使用其位置矩阵绘制多维数据集。不幸的是,这意味着每帧中有数千次调用glDrawArrays。

这有更好的技巧吗?我不知道如何将所有块组合成一个单独的glDrawArrays调用,因为这意味着VBO需要大量分配,为每个立方体添加所有顶点数据,并且不可能知道多少VBO在绘制它们之前需要的空间。我想的是为每500个左右的块分配一个VBO,这样如果它需要更多的空间用于块,它总能为它创建一个新的VBO。这样它就不会分配太多额外的空间,因为它只会为500个块分配足够的空间,这样,如果我们在这个世界上拥有5000个块,那么只有10个调用glDrawArrays而不是数千个那些电话。

我的另一个想法是,不是为立方体设置VBO,而是为四边形制作VBO,并在每个四边形上使用变换矩阵。这将需要更多的glDrawArrays调用,因为我必须为多维数据集的每个面调用它,但正好这样我可以删除已经有一个块旁边的面。对于楼层,每个街区周围有4个街区,因此实际上不需要绘制这4个面。这将节省为每个块绘制4个四边形,但它需要的glDrawArrays调用量的两倍以上。为了减少glDrawArrays调用的数量,我可以为每500个左右的四边形创建一个新的VBO,并在必要时向当前的VBO添加/删除四边形。这会减少glDrawArrays调用的数量,但这意味着我必须根据其纹理对每个四边形进行分组,这是另一个问题,因为如果我必须为每个纹理创建一个VBO,那将需要我分配很多额外不必要的空间,因为可能只有一个块使用某个纹理,我最终可能为该纹理分配500个块的空间。

这些是我对可以考虑优化渲染的一些方法的想法,但我不认为这些技术中的任何一种都会大大提高游戏的fps,因为每种方法都有自己的问题。有什么我没有想到的可能是更好的解决方案吗?

编辑:我切换到渲染四边形而不是立方体,因为这样我可以跳过不可见的面。之后我还添加了视锥体剔除,以便只显示视锥体内可见的块。这提高了性能,因此我现在可以以30 fps渲染体面的世界。但我认为还有很大的改进空间,因为目前有23,000个调用glDrawArrays(GL_TRIANGLES)(每个四边形渲染一个屏幕)。切换到使用glDrawArrays(GL_TRIANGLE_STRIPS)会有什么真正的区别吗?并且创建VBO的每个可以容纳1,000个四边形而不是仅仅1个四边形是可能的,但这意味着我将不得不在VBO中分配更多的空间。 (现在VBO中只存储了一个四边形,由矩阵转换为位置/旋转)。

3 个答案:

答案 0 :(得分:2)

如果使用八叉树(绝对是那种方式)不适合您,您可以优化调用vbo列表的代码。

在我的工作中,我开始以3fps的速率渲染场景,只是优化opengl调用和上下文切换,现在以53fps运行(考虑到起点,这是非常好的。)

所以,尽量不要在调用之间更改gpu中的任何寄存器:

  • 使用相同着色器对所有对象进行排序,以便仅使用一个glUseProgram来渲染所有对象
  • 订购具有透明度的对象,因此您只能在最后绘制半透明对象。
  • 以这样的方式绘制对象:片段仅绘制一次(如果对象在另一个之后,先绘制前面的对象,导致深度测试比片段计算更快)。
  • 使用没有"丢弃的着色器;"这对cpu来说是昂贵的。
  • 使用反向循环来获得一点cpu速度
  • 如果纹理已经与GPU中选择的相同(cpu'如果'成本低于GPU寄存器更改),则不选择纹理。
    • 如果不需要,请尽量不更新着色器属性(cpu如果成本更低)。

如果您发布一些代码,我可以帮助您更好。

答案 1 :(得分:0)

我目前正在使用OpenGL 4.x在普通PC上使用java实现体素世界。

一开始我遇到了同样的问题,但我遵循了一个非常基本的教程:https://sites.google.com/site/letsmakeavoxelengine/

每个块有一个渲染调用,没有问题,渲染了10个32 * 32 * 32块的块(FPS> 30)。您应该加载Chunk并仅将那些未被其他面遮挡的面(以便它们对播放器可见)添加到将上载到VBO的数组中。因此,每个Chunk只有一个rendercall,其面部数量最少

在2D中看起来像这样

    _ _ _
   |B B B|
   |B B |
   |B B B|
    - - - 

无需在外面之间绘制面。此外,您可以使用截头剔除:How to check if an object lies outside the clipping volume in OpenGL?

所以你只需要为那些实际存在于你的截头体内的那些块进行渲染调用。不要在相机后面渲染块。 OpenGL会对块的所有顶点进行大量计算,但是块不可见,所以为什么要首先渲染它。这可能发生在你的java代码中。

第三种优化可能是延迟着色:http://en.wikipedia.org/wiki/Deferred_shading

据我所知,在深度测试之前处理阴影并抛弃那些被其他人遮挡的三角形/面部,你可以使用延迟着色加速着色器,因为你只遮蔽那些将通过深度测试的顶点。

有很多方法可以优化体素渲染,但对我来说这是最基本的操作。第一个链接背后的给定教程尚未完成,但他展示了许多优化体素渲染的想法。

编辑: 如果你想使用纹理,每个立方体使用不同的纹理,我建议将所有纹理放在一个大的,所以你不需要交换纹理,简单的纹理查找比交换纹理要快得多(glBindTexture(。 。))然后进行查找,然后交换回此纹理。使用一个巨大的巨大纹理并将正确的UV坐标应用于顶点。

答案 2 :(得分:0)

你应该使用BSP Octrees来丢弃大块的屏幕外立方体。 你将世界划分为8个“空间立方体”,它们位于不同的轴上。 然后,检查相机是否可以看到立方体内的某些东西,如果它不能丢弃该部分中的所有块(可以加速到8倍)。然后,在块内,再次分为8个部分,再次检查它们是否可见。等等,加快检查和渲染。

http://en.wikipedia.org/wiki/Octree

http://i.ytimg.com/vi/S-oIeUiw2UY/hqdefault.jpg

使用“门户”可加速八叉树(我并不是指GladOs;))根据门窗内的能见度,丢弃体素和八度,但仅适用于室内设计。