3D Computer Grapihcs Using OpenGL - 15 Draw Element Instanced
友情提示:继续本节之前,需要保存此前的代码,本节为了试验,会对代码做一些修改,但后续的修改需要我们把代码返回之前的进度。
OpenGL内置支持Instancing,有专门的函数来处理这件事情。
为了方便,我们先使用最简单的三角形来学习
先修改sendDataToOpenGL()函数:
1 void MyGlWindow::sendDataToOpenGL() 2 { 3 GLfloat tri[] = { 4 -1.0f,+0.0f, 5 -1.0f,+1.0f, 6 -0.9f,+0.0f 7 }; 8 9 GLuint vertexBufferID; 10 glGenBuffers(1, &vertexBufferID); 11 glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); 12 glBufferData(GL_ARRAY_BUFFER, sizeof(tri), tri, GL_STATIC_DRAW); 13 glEnableVertexAttribArray(0); 14 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); 15 16 GLushort indices[] = { 0,1,2 }; 17 GLuint indexArrayBufferID; 18 glGenBuffers(1, &indexArrayBufferID); 19 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexArrayBufferID); 20 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 21 numIndices = 3; 22 }
相应的修改vertexshader
1 #version 430 2 3 in layout(location=0) vec2 position; 4 5 out vec3 passingColor; 6 7 void main() 8 { 9 10 gl_Position = vec4(position.xy,0,1); 11 passingColor= vec3(1,0,0); 12 }
以及paintGL()函数:
1 void MyGlWindow::paintGL() 2 { 3 glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 4 glViewport(0, 0, width(), height()); 5 6 glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0); 7 }
进行了上述修改之后,会在画布左上角绘制一个简单的红色三角形。
假如我们现在需要达到这样的目的:复制这个三角形多次,每次都横向偏移一定的量。
为了使用OpenGL提供的Instancing 方法,避免每次都调用glDrawElements,我们可以提供一个数组,作为偏移信息数组。
在sendDataToOpenGL()函数中定义一个GLfloat数组,把它绑定到GL_ARRAY_BUFFER上,开启一个新的通道,把数据传输进去。
最后在paintGL中使用glDrawElementsInstanced函数来绘制多个实例。
具体代码如下:
1 void MyGlWindow::sendDataToOpenGL() 2 { 3 GLfloat tri[] = { 4 -1.0f,+0.0f, 5 -1.0f,+1.0f, 6 -0.9f,+0.0f 7 }; 8 9 GLuint vertexBufferID; 10 glGenBuffers(1, &vertexBufferID); 11 glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); 12 glBufferData(GL_ARRAY_BUFFER, sizeof(tri), tri, GL_STATIC_DRAW); 13 glEnableVertexAttribArray(0); 14 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); 15 16 GLfloat offsets[] = {0.0f, 0.5f, 1.0f, 1.2f, 1.6f}; 17 GLuint offsetsBufferID; 18 glGenBuffers(1, &offsetsBufferID); 19 glBindBuffer(GL_ARRAY_BUFFER, offsetsBufferID); 20 glBufferData(GL_ARRAY_BUFFER, sizeof(offsets), offsets, GL_STATIC_DRAW); 21 glEnableVertexAttribArray(1); 22 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0); 23 glVertexAttribDivisor(1, 1); 24 25 GLushort indices[] = { 0,1,2 }; 26 GLuint indexArrayBufferID; 27 glGenBuffers(1, &indexArrayBufferID); 28 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexArrayBufferID); 29 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 30 numIndices = 3; 31 }
16-23行是新增内容。
16行定义了一个GLfloat数组,有5个元素。
17-20行我们已经很熟悉,不再解释了
21行开启了通道1
22行设定了通道1的数据格式,第一个参数表示通道1,第二个参数表示每1个数据作为一个单元
23行glVertexAttribDivsior是个新函数,它是配合实例化绘制的。没有它的情况下,tri数组每2个元素结合offsets数组的1个元素,也就是说offsets数组每个元素只用一次。有了这个函数之后,offsets数组的每个元素都要做一次循环,和tri数组的每组元素结合一次,形成5个批次。
修改VertexShader:
1 #version 430 2 3 in layout(location=0) vec2 position; 4 in layout(location=1) float offset; 5 6 out vec3 passingColor; 7 8 void main() 9 { 10 11 gl_Position = vec4(position.x + offset, + position.y,0,1); 12 passingColor= vec3(1,0,0); 13 }
增加了第4行,对应sendDataToOpenGL的新增通道1.
第11行中,我们把position的x坐标进行了offset的偏移。
最后修改paintGL()函数:
1 void MyGlWindow::paintGL() 2 { 3 glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 4 glViewport(0, 0, width(), height()); 5 glDrawElementsInstanced(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0, 5); 6 }
第5行使用glDrawElementsInstanced函数代替glDrawElements函数,进行了批量绘制。
第五个参数表示绘制五个实例。
最后效果: