Java 改进使用VBOs时所需的内存

Java 改进使用VBOs时所需的内存,java,opengl,vbo,Java,Opengl,Vbo,我一直在编写一个实验应用程序,其中我使用VBO渲染了100块16x16x16立方体。我之所以这么做,是因为有十几个人赞扬了超能力VBO,并告诉我它的性能比我在实际的Minecraft风格游戏中使用的每块显示列表要好得多 这是一个痛苦的过程,试图将许多只关注单个立方体/三角形的编写糟糕的教程改编成能够处理我需要的绘图量的内容。我仍然不相信VBO比显示列表更适合我的游戏 在大多数情况下,我最终对代码进行了调整,使交错的VBO数据只生成一次(当块加载时),然后每次render调用时,都会绑定缓冲区ID

我一直在编写一个实验应用程序,其中我使用VBO渲染了100块16x16x16立方体。我之所以这么做,是因为有十几个人赞扬了超能力VBO,并告诉我它的性能比我在实际的Minecraft风格游戏中使用的每块显示列表要好得多

这是一个痛苦的过程,试图将许多只关注单个立方体/三角形的编写糟糕的教程改编成能够处理我需要的绘图量的内容。我仍然不相信VBO比显示列表更适合我的游戏

在大多数情况下,我最终对代码进行了调整,使交错的VBO数据只生成一次(当块加载时),然后每次
render
调用时,都会绑定缓冲区ID并调用
glDrawArrays

我正在慢慢增加这个实验应用程序中的块/块的数量,看看性能如何处理。在实际游戏中,它必须在每个块中处理16x16x128块,最多加载20x20块。其中大约60%是渲染的实体块,因此可能有800万块。使用我开始使用的display list方法进行渲染时没有太多问题

但是,即使我现在的VBO渲染性能在可接受的水平内,我也无法在不达到内存限制的情况下生成10个块的半径:

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:658)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)
at org.lwjgl.BufferUtils.createByteBuffer(BufferUtils.java:60)
at org.lwjgl.BufferUtils.createFloatBuffer(BufferUtils.java:110)
at com.helion3.opengl.rendering.TextureQuadRenderer.<init>(TextureQuadRenderer.java:25)
at com.helion3.opengl.shapes.Chunk.<init>(Chunk.java:13)
at com.helion3.opengl.shapes.World.<init>(World.java:18)
at com.helion3.opengl.Game.start(Game.java:90)
at com.helion3.opengl.Launcher.main(Launcher.java:19)
线程“main”java.lang.OutOfMemoryError中的异常:直接缓冲内存 位于java.nio.Bits.reserveMemory(Bits.java:658) 位于java.nio.DirectByteBuffer。(DirectByteBuffer.java:123) 位于java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306) 位于org.lwjgl.BufferUtils.createByteBuffer(BufferUtils.java:60) 位于org.lwjgl.BufferUtils.createFloatBuffer(BufferUtils.java:110) 位于com.helion3.opengl.rendering.TextureQuanderer。(TextureQuanderer.java:25) 位于com.helion3.opengl.shapes.Chunk.(Chunk.java:13) 在com.helion3.opengl.shapes.World.(World.java:18) 位于com.helion3.opengl.Game.start(Game.java:90) 位于com.helion3.opengl.Launcher.main(Launcher.java:19) 我很有信心,我的缓冲区设置正确的计数需要。我呼吁:

BufferUtils.createFloatBuffer(
使用
192
每个立方体的浮动(3个顶点、3种颜色、2个纹理坐标乘以6个面和4个顶点/面)乘以
4096
-块测试中的块数

现在在真实的游戏中,我不会渲染没有暴露在空气中的块面-但即使在这个测试应用程序中这样做,我仍然只渲染16x16x16块

如何更好地管理VBO内存?我的VBO测试

VBO在哪一点上会像每个人都在推销的那样闪闪发光?哈哈

顺便说一下,我想我现在将深入研究实例,看看这有什么帮助

现在在真实的游戏中,我不会渲染没有暴露在空气中的块面-但即使在这个测试应用程序中这样做,我仍然只渲染16x16x16块

这很好。我只是提到了这一点,因为首先也是最重要的是,人们在编写Minecraft风格的渲染器时犯的一个常见错误是试图发送OpenGL所有的块(如果他们渲染所有的块,情况会更糟)。相反,你应该确定哪些曲面实际上是可见的,并且只在VBO中保留这些曲面。使用空间细分结构有助于此。像world这样的Minecraft只是散发着八叉树的气味,可以轻松地存储实际的东西。(这是为其他遇到此问答的人准备的)


另一件需要记住的事情是,由于所有的东西都呈现为立方体,所以你不必保留大约数百万个(相同的)立方体,只要翻译就可以了。如果你使用实例,一个立方体就足够了

使用实例化,每个块只需4个整数即可完整描述它(3个用于位置,1个用于曲面,从GL_纹理_2D_数组加载)。整数更可取,因为它们的种类较少(唯一的缺点是,较旧的GPU无法有效地处理它们)假设一个立方体代表1M,那么一个16位的整数给你一个(655 36m)的世界。你也不需要256个以上的表面来进行MiCrPad风格的游戏。所以,使用一个8位的整数来表示它。然后,当你只考虑可见的表面,那么块,很多你的体积不必驻留在VBO中。

这可能是一个真正的内存节约。从32位浮点到16位整数可以节省50%的内存。使用单个8位整数作为材质索引而不是3×32位浮点颜色可以将内存需求减少到1/12


为了进一步减少渲染负载,您可以利用世界上所有立方体都是平行的这一点。这使得消除隐藏曲面甚至不被处理变得非常容易:有8+6个主要方向可以查看立方体:8个方向上有3个共享一个角点的面可见,6个方向上只有您正在查看的曲面ng on Direct是可见的。从当前的角度来看,很容易确定每个案例适用的主平面。因此,您有14种多维数据集基础模板变体,并针对特定案例对每个子卷进行实例调用。八叉树再次帮助您选择哪些实例获得哪种变体


你应该考虑的另一件事是缓存一致性。多维数据集的数据在VBO中的排列和对齐方式以及访问它的顺序是非常重要的。一般来说,你希望数据能够很好地对齐和合并(尽管在当前的多路径内存体系结构中,分离的数据布局也可以提供相当好的性能)。但是,您的访问模式不应该“到处跳跃”,可以这么说。保持访问的良好分组。这是显示列表优于VBOs的主要原因:内容是恒定的,驱动程序可以将其内容重新排列为最佳对齐和有序的结构。您必须对此进行试验


给你