【问题标题】:How to use properly VAO and VBO in a voxel engine如何在体素引擎中正确使用 VAO 和 VBO
【发布时间】:2013-02-23 02:32:32
【问题描述】:

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

目前的引擎设计如下:

class Map -> 包含二维数组块(std::vector of 标准::向量)

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

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

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

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

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

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

【问题讨论】:

  • "std::vector of std::vector"...这对于密集的 2D/3D 数组通常不是很有效。切换到 boost.multiarray 之类的内容。
  • 我建议不要为每个 Block 类使用 draw 方法。虽然这是封装的一个很好的例子,但它有可能非常低效。特别是,对于渲染等操作,您可能必须在每个 draw 方法中冗余地设置状态(例如,绑定 VBO、更改着色器制服等),并且封装阻止了您可能如何优化该操作的任何全局视图。查看Visitor Pattern,它描述了一种以有组织的方式协调数据遍历的方法。
  • 你指的这个glVertexArray()函数是什么? OpenGL 有vertex arraysvertex array objectsvertex buffer objects。其中没有一个使用名为glVertexArray() 的东西。也许你在想glGenVertexArrays​()
  • 是的,我的意思是,很抱歉造成误解:D
  • @pqnet 通常,draw 方法与封装了绘制它们所代表的对象所需的所有状态的类相关联(当然,这取决于您如何实现它)。这种封装可能会导致向 OpenGL 发送冗余信息,因为使用相同状态(例如,着色器、纹理等)的对象都需要发送(或至少绑定)该状态。只是效率低下。为获得最佳性能,您需要一种对所有对象具有更“全局”视图的机制,并且可以尽量减少状态变化。

标签: c++ opengl


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多