简述
几何着色器在渲染管道中几何着色器位于片段着色之前,它接收的是一系列顶点组成的图元,并且以数组的形式输入。
基本的几何着色器
#version 330 core
//设置输入的图元类型为三角形
layout (triangles) in;
//设置输出的图元类型为三角形带,产生的最大顶点个数为3
layout (triangles_strip, max_vertces = 3) out;
void main()
{
int n;
//遍历所有输入的顶点
for(n = 0; n < gl_in.length(); n++)
{
//将输入位置拷贝到输出位置 上
gl_Position = gl_in[0].gl_Position;
//发射顶点数据
EmitVertex();
}
//完成图元
EndPrimitive();
}
布局限定符
layout (triangles) in;
layout (triangles_strip, max_vertces = 3) out;
第一行设置输入的图元类型为三角形,几何着色器会对每个需要渲染的三角形执行一次,程序中绘制命令所用的图元类型必须和几何着色器给定的图元类型兼容。如在程序中调用glDrawArrays(GL_TRIANGLES)绘制时,所用的类型是独立三角形,在几何着色其中输入的图元类型也为三角形,这两个图元类型之间必须互相兼容。
第二行中设置了输出的图元类型为三角形带,输出的最大顶点数为3。下表为程序中绘制命令的图元类型和何着色器给定的图元类型兼容性关系。
几何着色器图元类型 | 对应程序中制命令所用的图元类型 |
points | GL_POINTS |
lines | GL_LINES GL_LINE_LOOP GL_LINE_STRIP |
triangles | GL_TRIANGLES GL_TRIANGLE_STRIP GL_TRIANGLE_FAN |
lines_adjacency | GL_LINES_ADJACENCY GL_LINE_STRIP_ADJACENCY |
triangles_strip | GL_TRLANGLES_ADJACENCY GL_TRLANGLES_STRIP_ADJACENCY |
几何着色器函数
EmitVertex();
负责产生一个新的顶点作为几何着色器的输出。每次调用这个函数的时候,都会向当前条带(输出图元设置为line_strip)的末尾添加一个顶点。
EndPrimitive();
它中断了当前的条带,并且通知OpenGL在下一次调用EmitVertex();的时候重新开始一组新的线条
几何着色器的输入
几何着色器的输入来自于顶点着色器的输出,如果开启细分着色器,那么它的输入就会来自于细分着色器的输出。几何着色器对于每个输入的图元都会运行一次,前一个阶段的输出数据在几何着色器中都以数组的形式出现,包括用户定义的输入以及内置的输入变量gl_in。gl_in被隐士的声明为一个接口块。gl_in定义如下所示
in gl_PerVertex{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[]
}gl_in[];
gl_in的长度依赖于着色器中定义的输入图元类型,长度可以调用gl_in.length()来获取,
图元类型和长度对应关系
points图元类型长度为1
lines图元类型长度为2
triangles 图元类型长度为3
lines_adjacency图元类型长度为4
triangle_adjacencys图元类型长度为6
前三个类型分别为点,线和三角形,后两个表示邻接图元的形式,不能将邻接图元类型设置为几何着色器的输出类型。
几何着色器的输出
几何着色器的输出将数据送入到图元装配,光栅化器,进而进入片元着色器中,几何着色器输出变量的数量和顶点着色器相同。一般用顶点输出接口块定义gl_PerVertex。这个接口块的隐式声明如下
out gl_PerVertex
{
vec4 gl_Position;
float gl_PositSize;
float gl_ClipDistance[]
}
这里用到了接口块gl_PerVertex来声明几何着色器的输出,但没有名称,因此在本质上输出是位于全局空间内的。当然,用户定义的输出也可以进行声明,并且他会跟内置的接口块成员一起传到片元着色器中,每个几何着色器都可以创建多个输出点,所以必须显式的通过EmitVertex()函数来产生顶点。
除了内置和用户定义的顶点输出之外,几何着色器中还有三个特殊的内置变量类型可以输出到下一阶段,分别是gl_PrimitiveID, gl_Layer和gl_ViewportIndex。
产生图元
几何着色器的图元是由两个内置函数生成的,EmitVertex()和EndPrimitive()。每个几何着色器都必须调用EmitVertex()来生成图元。如果没有调用则不会生成任何图元,如果多次调用,则会输出多次,即输出多个图元。
几何体的裁剪
几何着色器可以有条件的抛弃一些几何体,这样就实现了裁剪
例如:只输出奇数位置的图元几何体
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
void main()
{
int n;
if(gl_PrimitiveIDIn & 1)
{
for(n = 0; n < gl_in.length(); ++n)
{
gl_Position = gl_in[n].gl_Position;
EmitVertex();
}
EndPrimitive();
}
}
几何体的扩充
正如前面了解到的,几何着色器可以多次调用EmitVertex()输出多种数量的图元,它产生的输出图元比输入的图元更多,这种方法叫扩充。
几何着色器的扩充不是无限的,OPenGL会有一个适当的上限值,限制一个几何着色器请求产生的顶点数量。上限值可以通过内置的着色器变量gl_MaxGeometryOuputVertices来获取,这个值最小为256,大规模的扩充对渲染性能影响较大,使用时需仔细权衡。
几何着色器的多实例化
当一个模型需要创建多个实例时,一般地可以在程序中循环调用glDrawArrays()函数完成多次绘制,这样,顶点着色器对每次输入的顶点运行一次,就会从内存中重复提取一次的数据,绘制多次相当于多次运行OPenGL整个管线。给GPU造成了潜在的处理负担。如果使用几何着色器,就只会多次运行几何着色器及以后的渲染管线,而不会运行全部的管线,效率会显著提升。当然,两种方法也可以同时使用。
多视口与分层渲染
几何着色器的两个特殊变量gl_ViewportIndex和gl_Layer可以将渲染结果重定向到帧缓存的不同区域,或者数组纹理的某一层,他们的值也可以作为片元着色器的输入使用。
在渲染管道中几何着色器位于片段着色之前,它接收的是一系列顶点组成的图元,并且以数组的形式输入。