欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

实例化

程序员文章站 2022-03-26 08:49:37
...

实例化

诞生

  • 如果要对同一物体渲染很多次,每次只是在世界坐标中的位置不一样,要是使用常规的绘制接口glDrawArrays或glDrawElements,那么当绘制的数量达到一定的程度后,就会出现卡帧
  • 卡帧的原因:OpenGL在绘制顶点数据之前,需要做很多的准备工作,比如告诉GPU该从哪个缓冲的哪个位置读数据,而且这些过程都是在CPU到GPU的总线上进行的,效率比较低,所以我们常说GPU渲染顶点很快,但命令GPU去渲染不一定快
  • 解决办法:将数据一次性发送到GPU,并且只使用一次绘制就让OpenGL利用那些数据绘制出多个物体,这种方法就是实例化
  • 总结一下:实例化允许我们使用一次渲染调用来绘制多个物体

使用

  • 将glDrawArrays和glDrawElements分别改为glDrawArraysInstanced和glDrawElementsInstanced
  • glsl在顶点着色器中定义了一个内置变量gl_InstanceID,在使用实例化渲染时,他从0开始,每渲染一个实例加1

实例化数组

  • 使用场景:在使用实例化渲染,往着色器发送uniform类型的数据,并且超过最大发送量时,他的一个代替方案是使用实例化数组
  • 实例化数组被定义为一个顶点属性,仅在顶点着色器渲染一个新的实例时才会更新
  • glVertexAttribDivisor(index, count) index表示顶点着色器中的某个顶点属性的索引,这个顶点属性的数据使用实例化数组存储的,count表示渲染多少个实例更新一次顶点属性的数据

一个栗子(实例化数组)

  • 顶点着色器
#version 330
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 offset;
main()
{
    gl_Position = vec4(position + offset, 1.0);
}
  • 客户端
glm::vec3 offset[10000] = {
    glm::vec3(1.0, 1.0, 1.0),
    glm::vec3(2.0, 2.0, 2.0),
    ...
    };
 unsigned int instanceVBO;//创建一个vbo,将实例化数据发送到vbo中
glGenBuffers(1, &instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * 10000, &offset[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FLASE, 3 * sizeof(float), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(1, 1);//第一个参数表示顶点着色器中变量offset的索引,第二个参数表示每一个实例更新一次
glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 10000);//调用一次绘制函数,实现绘制10000个三角形