写在前面
一直以来我们使用了顶点着色器(vertex shader)和片元着色器(fragment shader),实际上OpenGL还提供了一个可选的几何着色器(geometry shader)。几何着色器位于顶点和片元着色器之间,如果没有使用时,则顶点着色器输出到片元着色器,在使用几何着色器后,顶点着色器输出组成一个基础图元的顶点信息到几何着色器,经过几何着色器处理后,再输出到片元着色器。几何着色器能够产生0个以上的基础图元(primitive),它能起到一定的裁剪作用、同时也能产生比顶点着色器输入更多的基础图元。本节将学习几何着色器的基本用法,示例代码均可以从我的github下载。
几何着色器的基本概念
几何着色器在启用后,它将获得顶点着色器以组成一个基础图元为一组的顶点输入,通过对输入的顶点进行处理,几何着色器将决定输出的图元类型和个数。当输出的图元减少或者不输出时,实际上起到了裁剪图形的作用,当输出的图元类型改变或者输出更多图元时起到了产生和改变图元的作用。
要启用几何着色器,我们需要在之前的顶点和片元着色器基础上,将几何着色器GL_GEOMETRY_SHADER链接到着色器程序上,在代码上没有太大改动,你可以从我的github查看这个头文件。在程序中,我们创建一个包含上述3中着色器的程序:
// 准备着色器程序
Shader shader("scene.vertex", "scene.frag", "scene.gs");
一个直通的几何着色器
首先从一个基本的直通几何着色器来了解(以下简称gs)。这里我们绘制4个点,在gs中将这4个点的位置、大小信息原样输出到片元着色器。
顶点着色器如下:
#version 330 core
layout(location = 0) in vec2 position;
void main()
{
gl_Position = vec4(position, 0.5, 1.0);
gl_PointSize = 2.8; // 指定点大小 需要在主程序中开启 glEnable(GL_PROGRAM_POINT_SIZE);
}
几何着色器:
#version 330 core
layout(points) in ;
layout(points, max_vertices = 1) out;
// 直通的几何着色器 原样输出
void main()
{
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
EmitVertex();
EndPrimitive();
}
片元着色器:
#version 330 core
out vec4 color;
void main()
{
color = vec4(0.0, 1.0, 0.0, 1.0);
}
观察发现,在几何着色器中in和out分别指示了输入的图元,和输出的图元等参数。这里填写的是类型points表示输出点。从顶点着色器输入的图元类型,映射到几何着色器的输入模式如下表所示(参考自OpenGL SuperBible: Comprehensive Tutorial and Reference, 6th Edition):
几何着色器输入模式 | 顶点着色器输入 | 顶点最少个数 |
---|---|---|
points | GL_POINTS | 1 |
lines | GL_LINES, GL_LINE_LOOP, GL_LINE_STRIP | 2 |
triangles | GL_TRIANGLES, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP | 3 |
lines_adjacency | GL_LINES_ADJACENCY,GL_LINE_STRIP_ADJACENCY | 4 |
triangles_adjacency | GL_TRIANGLES_ADJACENCY,GL_TRIANGLE_STRIP_ADJACENCY | 6 |
同时从几何着色器输出模式,则有3种:
- points
- line_strip
- triangle_strip
这3种模式基本包含了所有绘图类型,例如triangle_strip就包含了triangle这种特例。max_vertices表示从几何着色器最多输出顶点数目,如果超过设定的这个数目,OpenGL不会输出多余的顶点。
在上述几何着色器中EmitVertex表示输出一个顶点,而EndPrimitive表示结束一个图元的输出,这是一对命令。gl_in是内置输入变量,定义为:
in gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
这是一个interface block,对这一概念不熟悉的可以回过头去查看uniform block这一节的内容。定义输入block为一个数组,因为输入的顶点要组成一个图元,因此通常不止一个。上面的例子中,使用一个顶点,因此我们使用gl_in[0]来获取这个顶点的信息。几何着色器中内置了一个输出变量,定义如下: