我一直致力于通过引入批处理来提高我的OpenGL ES 2.0渲染性能;特别是创建一个RenderBatch,在创建时指定纹理和着色器(现在)。这将状态设置为VAO以允许廉价的状态切换。我开始实现看起来像这样:
batch = RenderBatch.new "SpriteSheet" "FlatShader"
batch.begin GL_TRIANGLE_STRIP
batch.addGeometry Geometry.newFromFile "Billboard"
batch.end
batch.render renderEngine
然后它击中了我:我的Billboard文件有顶点,这些顶点是为了特定的实例使用而缩放和翻译的。所以我在addGeometry调用中添加了一个transform参数。
batch.addGeometry(Geometry.newFromFile("Billboard"), myObject.transform)
这解决了缩放,平移和旋转顶点的问题,但它首先查找顶点信息,通过变换矩阵对其进行变换,然后将其插入到批处理数据中。这虽然有效但似乎效率低下;它是CPU密集型的,并没有利用GPU的转换能力。但是,它有效,所以不是那么大的交易。 (尽管有更好的方法可以做到这一点会很高兴)
然而,我遇到了障碍:每个实例的纹理坐标可能也需要不同,这意味着我必须传入一个纹理变换矩阵,现在感觉很乱。
使用着色器处理对现有数据的这种转换是否有更简单的方法,这些着色器不限制给定的几何/模型,并且可以轻松扩展以使用法线贴图,UV贴图和其他花哨的技巧?谢谢!
答案 0 :(得分:2)
在我看来,你所说的是着色器uniforms
。通常,您将在VBO和VAO中为每个批次设置顶点数据和属性。然后,在渲染方法中,切换到正确的VAO并设置着色器制服。这些通常包括一个模型 - 视图 - 投影矩阵,用于将顶点转换为剪辑空间,这几乎每个帧都会改变,使用正确的纹理等等。
这是有效的,因为不变的顶点数据保存在GPU内存中,VAO负责廉价的状态切换,只有通常经常更改的制服会在每次渲染调用时发送到GPU。
如果您正在批处理需要单独模型视图投影矩阵的多个对象,那么您有几个选项:
您必须为需要单独的模型视图投影矩阵的每个批次执行单独的绘制调用
使用模型视图投影矩阵数组作为统一,并为每个对象提供一个属性,提供正确的投影矩阵索引以供使用
您必须使用CPU转换顶点并使用更新的数据重新填充VBO
第一种方法是首选方案,它将是高效和简单的。渲染大量绘制调用的缓慢部分通常是将数据从CPU传递到GPU,如果已经在VBO中拥有顶点数据,则每个对象的绘制调用的开销不会是一个大问题。这也解决了如何基于对象属性为每个对象提供不同制服的问题。在每个对象渲染方法中,相关属性在绘制调用之前设置为制服。如果每个对象都需要将不同的数据发送到GPU,那么这有什么用呢?
答案 1 :(得分:1)
这是一种权衡的情况。与CPU转换成本相比,由于批处理不足导致状态更改的成本。没有单一的最佳解决方案,但这取决于您的场景中有多少是静态的,多少是动态的以及如何布局。
一个常见的解决方案是将静态对象(相对于彼此的变换永远不会变成单个VBO)或少量VBO(如果它们使用不同的纹理,顶点格式等)完全转换。这在渲染之前完成一次。不是每一帧。然后,动态对象(玩家,怪物,等等)将单独渲染,并在顶点着色器中完成转换。
您仍然可以通过粗略地按纹理和程序对各个对象的绘制进行排序来优化状态更改。