在opengl red book中,我们知道glDrawArray和glDrawElements这些drawcall可以绘制点集合GL_POINTS,线段集合GL_LINES, 和三角形集合GL_TRIANGLES这些代数拓扑中的单纯形,这些是3D的最基本的"砖块",其他复杂的曲面曲线都是通过离散成线段集合,三角形集合通过opengl的drawcall来绘制的。
现在我们通过书上的讲解可以知道,如果要绘制n个曲面物体,例如[ball0, ball1, ball2, ... balli, balln], 假设这n个ball,不一样我们必须每个物体创建一个VBO,然后调用n次glDrawElements,就像下面的伪代码描述
ballmeshs = {ball0, ball1,ball2,...balln} for (int i = 0; i < n i++) { GLuint vbo; glGenBuffer(vbo); glBindBuffer(..., vbo); glBufferData(...,vbo, trimesh); ... glDrawElements(...); }
上面不是完整的opengl程序,只是说明性的,这样的绘制方式,主要的问题是这些API调用属于系统调用是有几十纳秒到几毫秒不等的,所以当n到了一定的数量,这个for里面的时间花费那是可观的,例如5秒是5000毫秒,几千个ball就能慢到秒级。
所以现代GPU渲染方式,采用VBO这种方式,则要把n个曲面物体放到整个三角形集合中,构造一个vbo,代码组织方式如下
ballmeshs = {ball0, ball1,ball2,...balln} // merge or (batch)n small mesh as one larger mesh trimesh = merge(ballmeshs); GLuint vbo; glGenBuffer(vbo); glBindBuffer(..., vbo); glBufferData(...,vbo, trimesh); ... glDrawElements(...);
这个merge函数是自己写的组织合并小的三角形mesh到一个大的三角形mesh,它也有时间花销,但是他的操作主要是内存操作,相比n个opengl的API及n个opengl的drawcall,反而是很快的了,当然CPU越快,merge就更快。
这里有个平衡的问题,就是如果n很大,全都merge成一个vbo,opengl由GPU制造商限制,vbo有个最大容量的,超过了有的显卡就报错,而且一个vbo,如果想删除这个vbo中的部分ball,其实是在内存块删除操作,会有时间花费,会慢,这样vbo就不能太大,一些经验值是推荐的,网上有相关介绍,可能像OGL红宝书只是编程指南,一般介绍不多,这里是推荐一篇gamedev的文章 https://www.gamedev.net/tutorials/_/technical/opengl/opengl-batch-rendering-r3900/ 这种就是给了个opengl的组织绘制数据的opengl代码,网上很多博客转载的比较多,但是要opengl比较熟悉才行. 我不知道有什么文献叫这个称呼,其实现代shader为主的GPU编程,主要的特点就是通过VBO, VAO, TBO给GPU一次喂更多的绘制数据,不然GPU一直喂不饱,浪费硬件,所以GPU叫批处理器是不是还更接近它的原理一些。