3

我目前正在构建一个体素引擎(例如 Minecraft),并且我正在使用 opengl。

引擎的当前设计如下所示:

类 Map -> 包含 2d 块数组(std::vector 的 std::vector)

类 Chunk -> 包含块的 3d 数组(也使用 std::vector)

类 Block -> 包含块的顶点。(立方体,由 12 个三角形组成);

我想要做的是让每个块都有一个 draw() 函数,它将绘制块中的所有块。我想我会使用 glGenBuffers() 为每个块创建一个缓冲区。从我阅读的教程中,应该首先使用 glVertexArray 创建一个顶点数组,然后绑定它,然后使用目标 GL_ARRAY_BUFFER 将缓冲区绑定到它并用数据填充它。

到目前为止,我设法使用使用 glGenBuffers() 在块中创建的缓冲区来渲染具有多个块的单个块,但在主函数中我创建 glVertexArray 并绑定它,而不是在块中调用 draw() 函数使用 glDrawArrays() 绑定块的缓冲区和绘图。

我的问题是,为每个块创建一个 VBO 是否正确?我也需要为每个块创建一个 glVertexArray 吗?或者我应该为所有块使用一个并且每次绑定另一个 VBO?

我试图通过教程和 opengl wiki 更多地研究 VAO 和 VBO,但到目前为止我还没有完全理解它是如何工作的以及应该如何正确使用它。

4

2 回答 2

2

我觉得你可能想得太早了。为什么你的地图被分成几块?您是否期待某种分页并非所有地图都在内存中?如果您的整个地图适合 GPU 内存,则每个地图使用一个缓冲区,并且不需要块。首先让你的渲染像那样。

之后,您可以考虑优化,例如仅绘制可见部分。或者以较低的细节绘制远处的部分。然后,您决定如何保留 GPU 缓冲区,何时更新或丢弃它们。这只是一个缓存问题。这不是一个简单的问题,而是一个缓存问题。

更实际的是:如果你按块组织,是的,每个块一个缓冲区。一个顶点和一个索引缓冲区。一次画一张。切换缓冲区对于绘图来说还不错。但是当你加载一个缓冲区时,你想加载整个东西,而不是部分。不要为大约 65k 顶点下的缓冲区而烦恼。

不过,第一步只是从 CPU 内存中提取数据。一旦你更好地了解了缓存的排列方式。不要过早将 GPU 结构混合到您的设计中。您还需要 CPU 结构来处理拣货或 AI 等事情。您应该有一个“基本事实”数据集,然后具有不同的分区和表示以满足不同的需求。

于 2014-10-05T13:26:11.517 回答
2

我知道我已经很晚了,但是如果将来有人发现这个问题,我会把这个答案放在这里。

Minecraft 的做法与人们想象的不同。与您在上面描述的带有嵌套缓冲区的系统不同,一个块可以简单地使用一个整数数组(如果您的块类型少于 256 个,则可以使用无符号字符)。如果它的“ID”出现在块数组中,您可以拥有每种类型的一个块,然后在特定位置渲染它,而不是为块提供许多对象。

它可以是一个 3D 数组(或在您的情况下是向量的向量,但这不是很有效)或一个可以通过宽度、高度和深度访问的 1D 数组。每个 ID(整数或无符号字符)都将链接到一种块类型。正因为如此,当你想渲染一个块时,你可以遍历块的所有维度——宽度、高度、深度——然后查看那个点的 ID。如果是 3,则渲染一块泥土,如果是 1,则将其制作为草块,如果为 0,则将其保留为空气块等。该系统非常适合塑造和生成地形,就像您一样重新操作一个整数数组。如果您按密度顺序排列块,那么您可以对块的区域进行一些算术以塑造地形(从块的该区域中的所有 ID 中减去一个,

这将通过实例化来完成,这样您就可以在一个块中拥有一个泥土块,并在需要的地方多次渲染它。在某种程度上,它甚至可以用制服来完成,这会改变方块的位置并一个一个地渲染它。

要回答您的问题,您需要一个定义块的位置数据和纹理坐标的VBO 。VAO 也是一个,用于存储该数据在内存中的排列方式。但在那之后,您可以在渲染每个块之前根据块中 blockArray 中的 ID 绑定不同的纹理。在加载时,根据你制作的块的大小,将所有污垢块的位置分组到一个块中,并绑定一次纹理,然后将所有污垢块渲染为一个(因为纹理绑定调用可能是一个性能问题,如果它们'发生得太频繁了)。

于 2016-08-05T18:21:14.937 回答