Skip to content

Latest commit

 

History

History
133 lines (84 loc) · 4.96 KB

09-instance.md

File metadata and controls

133 lines (84 loc) · 4.96 KB

实例化

一、预习

  1. 实例化

二、为什么要实例化

由于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

为了解决这个上限的问题,所以有了实例化数组这个东西,具体这个实例化数组是个什么东西,其实就是一个和colorposition一样的缓冲,开辟一段内存然后标记该如何解析这段内存

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的。

四、行星demo

下面来一个具体的案例

我们最终需要画一个上面的场景:

  1. 我们需要2个模型文件,第一个大行星和一个小行星;
  2. 先画中间的大行星;
  3. 周边的小行星可以看出一个小行星然后位移得到

具体可以看 此处代码地址