OPenGL--几何着色器的应用(多实例绘制)

时间:2024-03-21 21:12:03

简述

多实例渲染是一种连续执行多次相同的渲染命令的方法,并且每个渲染命令所产生的结果都会有轻微的差异。这是一种使用少量API来渲染大量几何体的有效方法。

当一个模型需要创建多个实例时,一般地可以在程序中循环调用glDrawArrays()函数完成多次绘制,这样,顶点着色器对每次输入的顶点运行一次,就会从内存中重复提取一次的数据,绘制多次相当于多次运行OPenGL整个管线。给GPU造成了潜在的处理负担。如果使用实例化方式创建,则只会多次运行几何着色器及以后的渲染管线,而不会运行全部的管线,效率会显著提升。当然,两种方法也可以同时使用。


常见的多实例绘制函数

glDrawArraysInstanced(),  glDrawElementsInstanced(), glDrawElementsInstancedBaseVertex()。

voidglDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primCount)

功  能:多实例绘制函数

返回值:void

参数:前三个参数是绘制几何体图元集的参数(与glDrawArrays()中的参数完全相同

primCount参数表示绘制实例的个数)

这个函数是glDrawArrays()的多实例版,这两个函数的参数完全是等价的,只是多了一个primCount参数,用于设置实例化的个数。其他绘制函数也有对应的实例化版本,函数glDrawElements()对应glDrawElementsInstanced(),函数glDrawElementsBaseVertex()对应glDrawElementsInstancedBaseVertex(),它们函数参数相同,只是实例化版的都会多一个参数设置实例化个数。

对于每个实例,在GLSL中的内置变量gl_InstanceID都会递增一次,新的数值会传递到顶点着色器中,以区分不同实例的顶点属性。

 

实例化实现思路

单纯实例化的实现非常简单,只需要调用上面实例化版本的绘制函数,并设置实例化个数就可以实现。但这样绘制的实例几乎是一摸一样的,包括位置颜色等属性,没有任何实际意义。

因此需要给每个实例都设置自己的属性,比如为每个实例设置不同的位置。

 

顶点着色器

1.这里的用uniform变量从程序中向实例传递数据,如果需要实例化100个,那么就在顶点着色其中声明一个数组来存储。

uniform vec2 offsets[100];

2.在原来的位置上进行偏移,根据GLSL中的内置变量gl_InstanceID索引每个实例的属性

gl_Position = vec4(aPos + offset[gl_InstanceID], 0, 1.0);

3.顶点着色器应该是这样的,其他着色器不变。

#version 330 core

layout (location = 0) in vec2 aPos;

layout (location = 1) in vec3 aColor;

 

out vec3 fColor;

uniform vec2 offsets[100];

 

void main()

{

   fColor = aColor;

   gl_Position = vec4(aPos + offsets[gl_InstanceID], 0, 1.0);

};

应用程序

在应用程序中要做的就是准备着色器中要的数据,并传递到着色器中,每个实例需要和数据一一对应。再调用多实例绘制函数就可以完成了。

1.准备数据

要实例化100个就需要准备100分数据,也是一个数组。

glm::vec2 translations[100];

int index = 0;

float offset = 0.1f;

for (int y = -10; y < 10; y += 2)

{

for (int x = -10; x < 10; x += 2)

{

glm::vec2 translation;

translation.x = (float)x / 10.0f + offset;

translation.y = (float)y / 10.0f + offset;

translations[index++] = translation;

}

}

2.传递数据

准备好数据后需要传到着色器中,应用程序设置uniform数据要有名称索引,所以需要拼接字符串和着色器中的变量意义对应。

for (unsigned int i = 0; i < 100; i++)

{

std::stringstream ss;

std::string index;

ss << i;

index = ss.str();

shader.setVec2(("offsets[" + index + "]").c_str(), translations[i]);

}

3.多实例绘制

最后调用多实例绘制函数完成绘制

glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 100);

 

效果

OPenGL--几何着色器的应用(多实例绘制)


参见:《OpenGL编程指南》第八版第3章

       LearnOpenGL教程