我使用OpenGL, glow和GLFW为一个项目编写一个简单的游戏。我已经有一个使用Assimp的3D模型导入器,它也可以处理纹理。
这是我绘制一个网格的方式:
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[NORMAL_VB]);
glNormalPointer(GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[TEXCOORD_VB]);
glTexCoordPointer(2, GL_FLOAT, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[POS_VB]);
glVertexPointer(3, GL_FLOAT, 0, NULL);
//Render the triangles
glDrawArrays(GL_TRIANGLES, 0, m_Entries[i].NumIndices);
我的问题是如何使用相同的网格渲染数百个移动实体,但每个实体都有不同的世界位置和旋转?
如果我有数百个移动的实体,每一帧我渲染相同的网格数百次,我可以注意到FPS现在下降了。
顺便说一句,我没有使用着色器,只是简单地绘制
由于glVertexPointer()
(和其他gl<Foo>Pointer()
调用)在opengl3和以后的版本中已被弃用,我假设您目前没有使用opengl3 +核心配置文件。
arb_draw_instance 提供glDrawArraysInstancedARB()
,它的工作原理基本上就像你已经使用的glDrawArrays
调用,但也提供了一个gl_InstanceId
整数变量在你的着色器中使用,然后你可以使用它来读取矩阵或任何其他你需要从UBO, TBO,或任何其他你想要使用的机制来获得每个实例的数据进入着色器。理论上,这个扩展可以在OpenGL 1.1的上下文中使用。
ARB_Instanced_Arrays提供glVertexAttribDivisorARB()
,它允许您修改特定顶点属性的工作方式。在顶点着色器中,每个后续顶点通常从附加的缓冲区中获取下一个属性值。因此,第一个顶点获得缓冲区中首先指定的位置,第二个顶点获得第二个位置,依此类推。使用这个函数,你可以告诉OpenGL根据实例而不是根据顶点来提供给着色器的数据。例如,你可以创建一个通用的顶点属性包含所有将要被绘制的实例的世界位置,并告诉OpenGL只在实例之间更新那个值,所以第一个实例的每个顶点获得第一个值,第二个实例的每个顶点获得第二个值,等等。从着色器的角度来看,这些值现在被视为顶点属性,而不是统一的。理论上,这个扩展可以早在opengl2的上下文中使用。
在我的硬件上,这两种方法都不能在OpenGL 2.1上下文中使用(因为这些扩展或相关的扩展没有暴露)。你的电脑可能和我的一样;无法在opengl2.1上下文中进行实例渲染。或者你的工作可能同时支持这两种方法。或者只是其中之一。同样,您将程序交给的其他人可能会发现他们的计算机要么支持其中一种,要么两者都支持,要么两者都不支持。扩展就是这样,无论主机支持什么,你的程序都应该能够应付。
在我的案例中,与其根据计算机支持的内容处理单独的实现,更简单的解决方案是切换到opengl3 +上下文,其中两个接口都以非扩展形式可用。
glDrawArraysInstanced()
(上述ARB接口的非扩展版本)在OpenGL 3.1中成为核心,因此保证在每个3.1核心配置文件中都存在。同样,glVertexAttribDivisor()
在OpenGL 3.3中被添加到核心配置文件中,并且将在每个3.3核心配置文件中可用。