OpenGL-场景实例化(1)

一、简介
本篇文章中,我们将带领大家实现一个小行星带的OpenGL应用程序。

二、实例化
实例化是一种只调用一次渲染函数却能绘制出很多物体的技术,它节省渲染物体时从CPU到GPU的通信时间,而且只需做一次即可。要使用实例化渲染,我们必须将glDrawArrays和glDrawElements各自改为glDrawArraysInstanced和glDrawElementsInstanced。这些用于实例化的函数版本需要设置一个额外的参数,叫做实例数量(Instance Count),它设置我们打算渲染实例的数量。这样我们就只需要把所有需要的数据发送给GPU一次就行了,然后告诉GPU它该如何使用一个函数来绘制所有这些实例。
就其本身而言,这个函数用处不大。渲染同一个物体一千次对我们来说没用,因为每个渲染出的物体不仅相同而且还在同一个位置;我们只能看到一个物体!出于这个原因GLSL在着色器中嵌入了另一个内建变量,叫做gl_InstanceID。
在通过实例化绘制时,gl_InstanceID的初值是0,它在每个实例渲染时都会增加1。如果我们渲染43个实例,那么在顶点着色器gl_InstanceID的值最后就是42。每个实例都拥有唯一的值意味着我们可以索引到一个位置数组,并将每个实例摆放在世界空间的不同的位置上。
我们调用一个实例化渲染函数,在标准化设备坐标中绘制一百个2D四边形来看看实例化绘制的效果是怎样的。通过对一个储存着100个偏移量向量的索引,我们为每个实例四边形添加一个偏移量。最后,窗口被排列精美的四边形网格填满:
在这里插入图片描述

每个四边形是2个三角形所组成的,因此总共有6个顶点。每个顶点包含一个2D标准设备坐标位置向量和一个颜色向量。下面是例子中所使用的顶点数据,每个三角形为了适应屏幕都很小:
GLfloat quadVertices[] = {
// —位置— ------颜色-------
-0.05f, 0.05f, 1.0f, 0.0f, 0.0f,
0.05f, -0.05f, 0.0f, 1.0f, 0.0f,
-0.05f, -0.05f, 0.0f, 0.0f, 1.0f,

-0.05f, 0.05f, 1.0f, 0.0f, 0.0f,
0.05f, -0.05f, 0.0f, 1.0f, 0.0f,
0.05f, 0.05f, 0.0f, 1.0f, 1.0f
};
片段着色器接收从顶点着色器发送来的颜色向量,设置为它的颜色输出,从而为四边形上色:
#version 330 core
in vec3 fColor;
out vec4 color;

void main()
{
color = vec4(fColor, 1.0f);
}
到目前为止没有什么新内容,但顶点着色器开始变得有意思了:
#version 330 core
layout (location = 0) in vec2 position;
layout (location = 1) in vec3 color;

out vec3 fColor;

uniform vec2 offsets[100];

void main()
{
vec2 offset = offsets[gl_InstanceID];
gl_Position = vec4(position + offset, 0.0f, 1.0f);
fColor = color;
}
这里我们定义了一个uniform数组,叫offsets,它包含100个偏移量向量。在顶点着色器里,我们接收一个对应着当前实例的偏移量,这是通过使用 gl_InstanceID来索引offsets得到的。如果我们使用实例化绘制100个四边形,使用这个顶点着色器,我们就能得到100位于不同位置的四边形。
我们一定要设置偏移位置,在游戏循环之前我们用一个嵌套for循环计算出它来:
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;
}
}
这里我们创建100个平移向量,它包含着10×10格子所有位置。除了生成translations数组外,我们还需要把这些位移数据发送到顶点着色器的uniform数组:
shader.Use();
for(GLuint i = 0; i < 100; i++)
{
stringstream ss;
string index;
ss << i;
index = ss.str();
GLint location = glGetUniformLocation(shader.Program, (“offsets[” + index + “]”).c_str())
glUniform2f(location, translations[i].x, translations[i].y);
}
这一小段代码中,我们将for循环计数器i变为string,接着就能动态创建一个为请求的uniform的location创建一个location字符串。将offsets数组中的每个条目,我们都设置为相应的平移向量。
现在所有的准备工作都结束了,我们可以开始渲染四边形了。用实例化渲染来绘制四边形,我们需要调用glDrawArraysInstanced或glDrawElementsInstanced,由于我们使用的不是索引绘制缓冲,所以我们用的是glDrawArrays对应的那个版本glDrawArraysInstanced:
glBindVertexArray(quadVAO);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100);
glBindVertexArray(0);
glDrawArraysInstanced的参数和glDrawArrays一样,除了最后一个参数设置了我们打算绘制实例的数量。我们想展示100个四边形,它们以10×10网格形式展现,所以这儿就是100.运行代码,你会得到100个相似的有色四边形。

三、总结
在本节中,我们讲述了何为实例化,接下来将会进入正题,千万不要走开。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值