实例化
在使用 glDrawArrays
或 glDrawElements
函数绘制顶点数据时,OpenGL
需要告诉 OpenGL
从哪里读取数据以及从哪里找顶点属性,这会非常消耗性能(因为这些操作都是在相对缓慢的 CPU
到 GPU
总线( CPU to GPU Bus )上进行的)。
这就导致一个问题,在绘制一个包含非常多(相同)模型的场景时,代码大致如下:
1 | for(GLuint i = 0; i < amount_of_models_to_draw; i++) |
这样,即使渲染(如,绘制简单的三角形)是瞬间完成的,但是在如此浩瀚的模型渲染时,更多的性能都消耗在了 CPU
到 GPU
总线上了。实例化就可以很好的解决此类问题。
实例化(Instancing):将数据一次性发送给 GPU
,然后使用一个绘制函数(glDrawArraysInstanced
或 glDrawElementsInstanced
)让 OpenGL
利用这些数据绘制多个相同的物体。
gl_InstanceID:每个实例都有唯一的 ID
,在使用实例化渲染调用时,gl_InstanceID
会从 0
开始,在每个实例被渲染时递增 1
。
例子,绘制100个2D四边形
顶点着色器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
layout (location = 0) in vec2 aPos; /* 顶点位置变量的属性位置值为:0 */
layout (location = 1) in vec3 aColor; /* 顶点颜色变量的属性位置值为:1 */
out vec3 fColor; // 输出,颜色
uniform vec2 offsets[100]; // 偏移
void main()
{
/* 顶点着色器内建变量:‘gl_InstanceID’
在使用实例化渲染调用(glDrawArraysInstanced 和 glDrawElementsInstanced)时,
gl_InstanceID 会从 0 开始,在每个实例被渲染时递增 1 */
vec2 offset = offsets[gl_InstanceID];
gl_Position = vec4(aPos + offset, 0.0, 1.0);
fColor = aColor;
}片段着色器:
1
2
3
4
5
6
7
8
9
10
in vec3 fColor; /* 输入,颜色值 */
out vec4 FragColor; /* 输出,指定片段颜色 */
void main()
{
FragColor = vec4(fColor, 1.0);
}偏移量计算并给 uniform 赋值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 计算 100 个偏移量
glm::vec2 translations[100];
GLint 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;
}
}
// 100 个 uniform 偏移量赋值
for(GLuint i = 0; i < 100; i++)
{
std::stringstream ss;
std::string index;
ss << i;
index = ss.str();
quadShader.setUniformVec2(("offsets[" + index + "]").c_str(), translations[i]);
}绘制:
1
2
3quadShader.use();
glBindVertexArray(quadVAO);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);