写在前面
前面我们学习了模型加载的相关内容,并成功加载了模型,令人十分兴奋。那时候加载的是少量的模型,如果需要加载多个模型,就需要考虑到效率问题了,例如下图所示的是加载了400多个纳米战斗服机器人的效果图:
渲染一个模型更多的实例,需要使用到实例化技术,就是本节要介绍的instancing object方法。本节示例代码均可以从我的github下载。
本节内容整理自:
www.learnopengl.com
渲染多个实例的方法
要渲染多个实例,基本的想法就是,在主程序中使用循环,在不同位置绘制多个物体,伪代码如下所示:
for(GLuint i = 0; i < instanceCount; ++i)
{
// 分别设置每个物体的模型变换矩阵 model matrix
// glDrawArrays(GL_TRIANGLES, ...)
}
这种方式存在的缺点是,当要渲染多个模型的实例时,需要多次调用glDraw这类命令,而这类命令从CPU–>GPU是需要花费时间的,因为使用绘制命令时OpenGL需要做一些工作,例如通知GPU从哪个buffer里面读取数据。虽然GPU绘图很快,但是CPU–>GPU的命令发送,当量比较大时还是会成为瓶颈。
因此OpenGL提供了glDrawArrays和glDrawElements的绘制实例版本,分别对应为glDrawArraysInstanced和glDrawElementsInstanced 。实例版本的函数,多了一个参数,就是最后一个指定渲染多少个实例的参数。
下面以一个简单的绘制多个矩形的例子作为引例,开始熟悉绘制多个实例。
使用多个uniform传递实例数据
假设我们要绘制100个矩形,在顶点着色器中,我们使用一个uniform数组:
#version 330 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec3 color;
uniform vec2 offsets[100]; // 每个实例的位移量
out vec3 fColor;
void main()
{
vec2 offset = offsets[gl_InstanceID]; // 通过gl_InstanceID索引每个实例的位移量
gl_Position = vec4(position + offset, 0.5f, 1.0f);
fColor = color;
}
通过gl_InstanceID来索引每个实例,而在主程序中,我们通过循环设置这个uniform数组的内容:
//准备多个实例的位移量数据
glm::vec2 translations[100];
int index = 0;
GLfloat offset = 0.1f;
for (GLint y = -10; y < 10; y += 2)
{
for (GLint x = -10; x < 10; x += 2)
{
glm::vec2 translation;
translation.x = (GLfloat)x / 10.0f + offset;
translation.y = (GLfloat)y / 10.0f + offset;
translations[index++] = translation;
}
}
// 接着 向shader传递这100个translate uniform
最后通过实例版本函数绘制多个矩形:
shader.use();
glBindVertexArray(quadVAOId);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); // 使用instance方法绘制
得到的效果如下图所示: