我构建了一个2D图形引擎,并为它创建了一个批处理系统,因此,如果我有1000个具有相同纹理的精灵,我可以通过一次调用openGl来绘制它们。
这是通过将具有相同纹理的所有精灵的所有顶点放入单个vbo顶点数组来实现的。
不是“打印这些顶点,打印这些顶点,打印这些顶点”,而是“将所有顶点放到另外,打印”,只是为了非常清楚。 很容易,但现在我正试图在3D中实现同样的事情,而且我遇到了很大的问题。
问题是我正在使用模型视图投影矩阵来放置和渲染我的模型,这是在3D空间中渲染模型的常用方法。
对于屏幕上的每个模型,我需要将MVP矩阵传递给着色器,以便我可以使用它将每个顶点转换为正确的位置。
如果我要在着色器之外进行转换,那么它将由cpu执行,这不是一个好主意,原因很明显。
但问题出在那里。我需要将矩阵传递给着色器,但对于每个模型,矩阵都是不同的。
所以我不能像使用2d精灵那样做,因为更改着色器制服需要每次画画。
我希望我已经清楚了,也许你有一个好主意我没有或你已经遇到了同样的问题。我知道某个地方有一个解决方案的事实,因为在像Unity这样的引擎中,你可以为多个模型使用相同的着色器,并通过一次绘制调用获得
答案 0 :(得分:8)
存在一个与您正在寻找的功能完全相同的功能,它被称为实例化。通过实例化,您可以在统一缓冲区中存储n
矩阵(或其他任何需要的内容),并调用glDrawElementsInstanced
来绘制n
个副本。在着色器中,您将获得一个额外的输入gl_InstanceID
,您可以使用该输入索引统一缓冲区以获取该特定实例所需的矩阵。
您可以在此处详细了解实例化:https://www.opengl.org/wiki/Vertex_Rendering#Instancing
答案 1 :(得分:3)
答案取决于每个项目的顶点数据是否相同。如果是,您可以在@ orost的答案中使用实例化,在顶点着色器中使用glDrawElementsInstanced
和gl_InstanceID
,并且应该首选该方法。
但是,如果每个3D模型需要不同的顶点数据(通常是这种情况),您仍然可以使用单个绘制调用渲染它们。为此,您可以使用glVertexAttribPointer
(和glEnableVertexAttribArray
)将另一个流添加到顶点数据中。这个额外的流将包含顶点在渲染时应该使用的统一缓冲区内的矩阵索引 - 因此VBO中的每个网格在额外流中都具有相同的索引。统一缓冲区包含与实例化设置中相同的数据。
注意,如果需要重做批处理,此方法可能需要一些额外的CPU处理 - 例如,不应再渲染批处理中的对象。如果经常需要这个过程,则应确定批量项目是否实际上是有益的。
答案 2 :(得分:0)
除了实例化并添加另一个顶点属性作为一些对象ID之外,我还想提一下另一种策略(虽然需要现代的OpenGL):
扩展程序ARB_multi_draw_indirect(自GL 4.3以来的核心)添加间接绘图命令。这些命令直接从另一个缓冲区对象中获取它们的参数(顶点数,起始索引等)。使用这些函数,可以使用单个绘制调用绘制许多不同的对象。
但是,由于您仍然需要像转换矩阵这样的每个对象状态,因此该功能还不够。但是结合ARB_shader_draw_parameters(尚未在核心GL中),您将获得gl_DrawID
参数,对于一个多绘制间接调用中的每个单个对象,该参数将增加1。这样,您可以索引存储每个对象数据的一些UBO,TBO或SSBO(或其他)。