几何着色器在顶点着色器和片元着色器中间。从下面的OpenGL渲染流程图可以看出,渲染的流程为顶点着色器—图元装配—视口剔除—光栅化—片元着色器。而几何着色器在光栅化阶段,但是在光栅化之前执行。
几何着色器的输入是一个图元的所有顶点,输出是图元类型和所有顶点。
#version 330 core
layout (points) in;
layout (line_strip, max_vertices = 2) out;
void main() {
gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0);
EmitVertex();
gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);
EmitVertex();
EndPrimitive();
}
上面的例程中,我们使用layout修饰从输入输出,输入类型定义为points,我们还可以使用下面这些类型。
points
:绘制GL_POINTS图元时(1)。lines
:绘制GL_LINES或GL_LINE_STRIP时(2)lines_adjacency
:GL_LINES_ADJACENCY或GL_LINE_STRIP_ADJACENCY(4)triangles
:GL_TRIANGLES、GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN(3)triangles_adjacency
:GL_TRIANGLES_ADJACENCY或GL_TRIANGLE_STRIP_ADJACENCY(6)
输出类型接受下面三种类型:
points
line_strip
triangle_strip
同时我们还需要在输出中使用max_vertices来修饰片元的最大顶点数。有了定点数局我们就可以使用EmitVertex函数和EndPrimitive函数来绘制我们的图元,EmitVertex函数是绘制一个顶点,EndPrimitive函数表示停止之前的绘制并合成图元。上面的例子中,连续线段的最大顶点数是2,所以绘制两个点就够了。
有了上面的功能,我们就可以任意修改图元的顶点,甚至修改图元的类型。我们可以输入一个顶点,输出连续三角形,那么,最后的结果是,之前定义的顶点出就会绘制出我们修改后的连续三角形。
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;
void build_house(vec4 position)
{
gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:左下
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:右下
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:左上
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:右上
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:顶部
EmitVertex();
EndPrimitive();
}
void main() {
build_house(gl_in[0].gl_Position);
}
上面的例子展示的是将输入的顶点转化为一个小房子状的连续三角形(triangle_strip),小房子一共3个三角形,那么最大定点数是3 + 2。我们以输入的顶点坐标为初始位置,以不同的位置偏移发射小房子的5个顶点,最后组成图元就有结果了。
有一点需要注意的是,几何着色器的输入图元类型是由我们在渲染循环里调用glDrawArray和glDrawElement是设置的绘制模式对应的,也即如果要让上面那个几何着色器生效,就需要在绘制时使用GL_POINTS。
还有一点需要注意,顶点着色器与几何着色器间的数据传输,我们需注意它的使用方式,以接口块为例。
// 顶点着色器接口块定义
out VS_OUT {
vec3 color;
} vs_out;
// 几何着色器接口块定义
in VS_OUT {
vec3 color;
} gs_in[];
上面的例子中给出了两处的定义方式,需要注意的地方在几何着色器中定义的是一个数组。这是因为顶点着色器处理的是单个顶点理,它的输入是一个顶点的位置。而几何着色器处理的是一个图元,他输入的是一个图元的一组顶点。
我们可以在几何着色器中修改顶点颜色,需要注意的是,只要我们修改了输出到片段着色器的颜色,那么该几何着色器后面绘制的顶点颜色都是修改后的颜色,除非再次修改颜色。
fColor = gs_in[0].color;
gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:左下
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:右下
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:左上
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:右上
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:顶部
fColor = vec3(1.0, 1.0, 1.0);
EmitVertex();
EndPrimitive();
爆破物体
使用几何着色器,将顶点沿着法线方向运动一段距离。我们主动传入法向量,或者自己计算法向量,已知三角形的三个顶点计算法向量是很简单的,只是需要注意规范化。
法向量可视化
顾名思义就是看得见法向量,思想是,先绘制一遍原物体,然后再绘制出法线,绘制法线时是用原物体的顶点数据,增加几何着色器,在几何着色器里把法线画出来即可。
#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices = 6) out;
in VS_OUT {
vec3 normal;
} gs_in[];
const float MAGNITUDE = 0.4;
void GenerateLine(int index)
{
gl_Position = gl_in[index].gl_Position;
EmitVertex();
gl_Position = gl_in[index].gl_Position + vec4(gs_in[index].normal, 0.0) * MAGNITUDE;
EmitVertex();
EndPrimitive();
}
void main()
{
GenerateLine(0); // 第一个顶点法线
GenerateLine(1); // 第二个顶点法线
GenerateLine(2); // 第三个顶点法线
}
上面的例程需要注意的是,我们输入图元是三角形,输出是连续线段,最大顶点是6,而代码里在EndPrimitive之前只发射了两个顶点,所以最大顶点数设置为2就足够了。