实例化渲染
实例化渲染适用于使用同一个模型渲染多次的情景,比如草地,一堆岩石等。
1、先看一个渲染方形100次的例子:
其顶点着色器会有少许适应实例化渲染的变化
#version 330 core layout (location = 0) in vec2 aPos; layout (location = 1) in vec3 aColor; out vec3 fColor; uniform vec2 offsets[100]; void main() { vec2 offset = offsets[gl_InstanceID]; gl_Position = vec4(aPos + offset, 0.0, 1.0); fColor = aColor; }
可以看到着色器中传入了100个偏移向量。顶点着色器中内置了一个变量gl_InstanceID,此变量表示当前绘制的实例编号(从0开始)。那么我们可以用此内置变量索引对应的偏移向量,用来在屏幕不同的地方绘制方形。
向顶点着色器中传入100个偏移向量的代码如下:
shader.use(); for(unsigned int i = 0; i < 100; i++) { stringstream ss; string index; ss << i; index = ss.str(); shader.setVec2(("offsets[" + index + "]").c_str(), translations[i]); }
渲染代码如下:
void Quad::Draw_Instancing() { glBindVertexArray(VAO); glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); glBindVertexArray(0); }
2、使用实例化数组
向着色器中传入uniform数据是有最大数量限制,我们可以使用实例化数组传入数据,可以解决数量限制问题,当然还是会限制于内存大小的,只要内存允许就可以极大数量的传入数据。
顶点着色器如下:
#version 330 core layout (location = 0) in vec2 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aOffset; out vec3 fColor; void main() { vec2 pos=aPos*(gl_InstanceID/100.0); gl_Position = vec4(pos+aOffset, 0.0, 1.0); fColor=aColor; }
现在去掉了uniform 偏移向量,改为由顶点属性传入了
layout (location = 2) in vec2 aOffset;
那么同其它顶点属性一样得将偏移向量存入缓存
glGenBuffers(1, &this->instanceVBO); glBindBuffer(GL_ARRAY_BUFFER, this->instanceVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 100, &this->translations[0], GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0);
设置顶点属性指针
glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, this->instanceVBO); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexAttribDivisor(2, 1); glBindVertexArray(0);
可以发现现在比以往多加了一行代码
glVertexAttribDivisor(2, 1);
其中第一个参数指顶点属性2,第二个参数可以取0,1,2……。当取值0表示逐顶点更新,也就是默认的方式;取值1时表示逐实例更新;其它值x,表示每x个实例更新一次。
3、看一个复杂点的例子,绘制行星带
其中包含一个大的行星,然后一个环绕它的岩石带。这些岩石是一样的,我们当然可以将一个岩石绘制多次,每个岩石只有model矩阵不同,这样的话在我的机器上可以正常绘制10000个岩石,帧率还能在36帧。
使用实例化渲染后可以正常渲染1000000个岩石,帧率在20帧,可以看出实例化渲染可以比正常渲染快100倍,当然这是就这个例子而言。
实例化渲染需要在顶点着色器中的顶点属性中传入model矩阵
layout (location = 3) in mat4 model;
传入着色器的代码如下:
void PlanetaryBelt::setupBuffer(Model* obj) { glGenBuffers(1, &buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, this->amount * sizeof(glm::mat4), &this->modelMatrices[0], GL_STATIC_DRAW); std::vector<AssimpMesh>* meshes = obj->assimpModel->GetAssimpMesh(); for (unsigned int i = 0; i < meshes->size(); i++) { glBindVertexArray(meshes->at(i).GetVAOBuffer()); GLsizei vec4Size = sizeof(glm::vec4); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)0); glEnableVertexAttribArray(4); glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(vec4Size)); glEnableVertexAttribArray(5); glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(2 * vec4Size)); glEnableVertexAttribArray(6); glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(3 * vec4Size)); glVertexAttribDivisor(3, 1); glVertexAttribDivisor(4, 1); glVertexAttribDivisor(5, 1); glVertexAttribDivisor(6, 1); glBindVertexArray(0); } }
可以看到代码较之前略有变化,因为顶点属性允许的最大数据大小为vec4,所以我们要在顶点属性位置3,4,5,6分别传入矩阵的一列值,然后在着色器中获取一个mat4。
原文地址:https://www.cnblogs.com/chen9510/p/11474932.html
- 编程思想 之「对象漫谈」
- Github 项目推荐 | TensorFlow 概率推理工具集 —— probability
- Github 项目推荐 | 用于 C/C++、Java、Matlab/Octave 的特征选择工具箱
- Mercari Price 比赛分享 —— 语言不仅是算法和公式而已
- Github 项目推荐 | GAN 的 Keras 实现案例集合 —— Keras-GAN
- Github 项目推荐 | 微软开源 MMdnn,模型可在多框架间转换
- 半自动化运维之动态添加数据文件(一) (r5笔记第55天)
- 半自动化运维之动态添加数据文件(二) (r5笔记第56天)
- 11g Active DataGuard初探(r5笔记第54天)
- Github 项目推荐 | 用于构建端对端对话系统和训练聊天机器人的开源库 —— DeepPavlov
- 我身边的一些数据库事故 (r5笔记第52天)
- 一个清理脚本的改进思路(r5笔记第51天)
- 【专业技术】Python爬虫:抓取手机APP的传输数据
- 海量数据迁移之传输表空间(一) (r5笔记第71天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法