2013-08-07 51 views
4

我在XGL/MonoGame接口的启发下在OpenGL中制作2D批处理渲染器,但我遇到了一个小的设计问题,我正在寻找一些输入。目前,您可以通过以下四种方式一般提交的顶点数据:在OpenGL批处理渲染器中批处理任意顶点数据

void Render(const Sprite& sprite); 
void Render(const Shape& shape); 
void Render(const Vertex* vertices, unsigned int length); 
void Render(const Vertex* vertices, unsigned int length, const Texture* texture); 

雪碧包含四个顶点,颜色和纹理坐标,而其他三个可以包含任意数量(精灵和形状有独特的转换)。一切都可以纹理或无纹理。我想批量处理一切,以减少状态变化和OpenGL绘制调用的次数。我认为有理由认为大多数提交将共享顶点,以便我可以使用glDrawElements而不是glDrawArrays,但是我无法根据上述描述正确地批量处理事物。

XNA/MonoGame sprite batchers的工作原理是,它们只与纹理四边形/三角形一起工作,而不是任意形状。或者,我可以像SFML渲染器那样做,并为每个可绘制对象发出绘图调用,但这会破坏批渲染的目的。

我觉得我的渲染器试图“做所有事情”,这是我想避免的,因为它通常会很快变得过于复杂。

我问的是:我怎么能重新设计我的渲染器?我可以为不同的提交保留单独的批次清单吗?我能以某种方式模块化渲染器吗?我应该只允许在XNA/MonoGame中完成纹理对象吗?

回答

6

好的,所以我们需要最小化状态改变的次数并绘制调用。我假设你使用的是现代OpenGL,包括Vertex Buffer Objects和着色器。

一种方法是确保所有顶点数据具有相同的格式。例如,每个顶点都有一个位置,颜色和纹理坐标(xyz,rgba,uv)。如果我们在VBO中交织顶点数据,则在渲染之前,我们只需要调用glVertexAttribPointerglEnableVertexAttribArray

这意味着无纹理对象的某些冗余数据,但我们得到的所有东西都塞进一个批次,这是很好的。

为了处理无纹理对象,你既可以绑定一个空白的纹理并把它当作一个纹理对象。或者,您可以在片段着色器中使用统一变量(0和1之间的浮点数),并使用mix函数在纹理颜色和顶点颜色之间进行混合。

要批量精灵和形状,我们应该首先处理CPU上的转变,使我们始终上传的“世界”坐标 - 到GPU。这使我们不必为每个精灵设置一个变换均匀,每个精灵都需要单独的绘图调用。

此外,我们需要的质地尽可能进行排序,作为纹理绑定是更昂贵的操作,你可以做之一。

我们的做法基本上可以归结为以下几点:

  • 持空单Vertex-和索引缓冲区对象存储数据
  • 将所有的顶点数据在一个单一的格式和VBO交织数据
  • 排序纹理
  • 冲洗的数据(绘制元件/阵列)中每当我们改变织构(或设置纹理混合均匀,如果我们用该选项去)缓冲器

从CPU获取数据到GPU内存可以通过不同的方式完成。例如,首先在GPU上分配足够大,空的内存缓冲区,并使用glBufferSubData上传顶点/索引数据的子集,无论何时执行一次Render调用。

请记住个人资料!

做这类工作时进行性能分析是非常重要的。例如,比较批处理和单个绘图调用之间的性能,或比较glDrawArrays与glDrawElements之间的性能。我推荐使用gDebugger,这是一款免费且非常好的OpenGL分析器。

另请注意,过大的VBO可能为hurt your performance。因此,保持它的尺寸合理,并在每次填满时以平局调用。

+0

这是一些非常可靠的信息,非常感谢。我实际上总共创建了三个渲染器(支持用于教育目的的不同版本),但我的看法转化得很好,据我所知。我的顶点具有相同的格式。当使用混合函数时,我只会插值0和1,完整的纹理颜色或完整的顶点颜色,对不对?转换完全按照您的建议处理。我还考虑过在批处理渲染之前的某个时间将未经过处理的对象复制到批处理中。我不知道gDebugger,感谢您的链接! – NordCoder