由于OpenGL
在绘制顶点数据之前需要做很多准备工作(比如告诉GPU该从哪个缓冲读取数据,从哪寻找顶点属性,而且这些都是在相对缓慢的CPU到GPU总线(CPU to GPU Bus)上进行的)。所以,即便渲染顶点非常快,命令GPU去渲染却未必
为了减少数据从CPU到GPU的传输,使用glDrawArraysInstanced
或者glDrawElementsInstanced
比如要画下面的场景
上面我们是通过在着色器中定义Uniform
变量来传递数据的,但是不同的着色器的Uniform
是有上限的,如果我们的数据超过了这个数量,那么数据将无法传递了,各个着色器的上面分别是下面的宏查看
GL_MAX_VERTEX_UNIFORM_COMPONENTS// 顶点着色器 0x8B4A
GL_MAX_GEOMETRY_UNIFORM_COMPONENTS // 几何着色器 0x8DDF
GL_MAX_FRAGMENT_UNIFORM_COMPONENTS // 片段着色器 0x8B49
为了解决这个上限的问题,所以有了实例化数组这个东西,具体这个实例化数组是个什么东西,其实就是一个和color
和position
一样的缓冲,开辟一段内存然后标记该如何解析这段内存
GLuint instanceVBO;
glGenBuffers(1,&instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER,instanceVBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(translations) ,translations,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ARRAY_BUFFER,instanceVBO);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,2 * sizeof(float),(void *)0);
到这里都没什么新的东西,要想开启实例化数组,那么要使用下面的这个函数
+ glVertexAttribDivisor(2, 1);
第一个参数表示的是索引值,第二个表示的是没几个实例更新一次数据,搭配glDrawArraysInstanced(GL_TRIANGLES,0,6,100);
就能画出100
个矩形了,如果我们去掉glVertexAttribDivisor
看看会绘制出什么呢
...
glGenBuffers(1,&VBO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(quadVertices),quadVertices,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
GLuint instanceVBO;
glGenBuffers(1,&instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER,instanceVBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(translations) ,translations,GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
glGenVertexArrays(1,&VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE, 5 * sizeof(float), (void *) 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE, 5 * sizeof(float), (void *) (2 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER,instanceVBO);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,2 * sizeof(float),(void *)0);
- glVertexAttribDivisor(2, 1);
造成这样的结果不是bug,是因为一旦我们不调用glVertexAttribDivisor
,就会当做普通的属性处理,由于我们的场景是通过6个点画出2个三角形,正好是这6个点,大家可以自己算下这6个点的位置。
再修改代码如下
...
glBindBuffer(GL_ARRAY_BUFFER,instanceVBO);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,2 * sizeof(float),(void *)0);
- glVertexAttribDivisor(2, 1);
+ glVertexAttribDivisor(2, 2);
因为现在是没2个实例更新一个offset的。
下面来一个具体的案例
我们最终需要画一个上面的场景:
- 我们需要2个模型文件,第一个大行星和一个小行星;
- 先画中间的大行星;
- 周边的小行星可以看出一个小行星然后位移得到