实践项目:https://github.com/JackieLong/OpenGL/tree/main/project_geometry_shader_test
在顶点着色器和片段着色器之间有一个可选的几何着色器。输入是一个图元的一组顶点(三角形的三个顶点),它能这组顶点变换为不同的图元,还可以生成更多的顶点。
点击查看【processon】
一个简单的几何着色器例子如下:
#version 330 core
// 输入的图元类型,不同类型也指定了图元包含的最少顶点数
layout (points) in; // glDrawArrays(GL_POINTS, ...)时,输入的图元顶点,以下同理。
// 最少顶点数1。
layout (lines) in; // GL_LINES、GL_LINE_STRIP,最少顶点数2。
layout (lines_adjacency) in; // GL_LINES_ADJACENCY、GL_LINE_STRIP_ADJACENCY,最少顶点数4。
layout (triangles) in; // GL_TRIANGLES、GL_TRIANGLES_STRIP、GL_TRIANGLES_FAN,最少顶点数3。
layout (triangles_adjacency) in; // GL_TRIANGLES_ADJACENCY、GL_TRIANGLE_STRIP_ADJACENCY,最少顶点数1。
// 输出图元类型,max_vertices表示最大输出顶点数量,不会绘制超出的顶点。下面的数字仅仅是例子。
layout (points, max_vertices = 2) out;
layout (line_strip, max_vertices = 4) out;
layout (triangle_strip, max_vertices = 3) out;
// ******************************
// 这是前一着色阶段的输出
in gl_Vertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[]; // 数组,一般图元都包含多于1个顶点
// ******************************
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(); // 将发射的顶点按上面的输出类型,组成相应图元
}
简单示例
这里展示一个顶点着色器、几何着色器、片段着色器三者联合使用的例子,绘制指令如下:
//顶点数据:4个顶点,组成三个三角形,画出一个最简单的房子。
float vertices_points[] =
{
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // 左上
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // 右上
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 右下
-0.5f, -0.5f, 1.0f, 1.0f, 0.0f // 左下
};
......
while(!glfwWindowShouldClose()){
......
shader.use();
glBindVertexArray( VAO_points );
glDrawArrays( GL_POINTS, 0, 4 ); // 简单的绘制4个点。
......
}
......
输出结果如下,虽然绘制指令是绘制4个点,但是最终输出远不止4个顶点,而且还是三角形图元。
顶点着色器代码如下:
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColor;
// 数据传递方式一:以接口块的方式传递数据到下一个着色器阶段(几何着色器)
out VS_OUT{
vec3 color;
} vs_out;
// 数据传递方式二:
out vec3 color;
void main()
{
vs_out.color = aColor;
color = aColor;
gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
}
几何着色器代码如下:
#version 330 core
// *******************************
// 一、从上一个着色器阶段接收的输入数据
// *******************************
in VS_OUT{
vec3 color;
} gs_in[]; // 注意都是数组,因为几何着色器接收的是组成一个图元的顶点数据,哪怕只有一个顶点,也是在数组中
in vec3 color[];
// *******************************
// 二、指定接收的输入图元类型
// *******************************
layout(points) in; // 几何着色器的输入是点图元。
// *******************************
// 三、指定接收的输入图元类型
// *******************************
layout(triangle_strip, max_vertices = 5) out; // 几何着色器的输出图元是5个点构成的三角形带(就是一个简单房子形状)
// *******************************
// 四、指定输出到下以着色器阶段的数据
// *******************************
out vec3 fColor; // 输出到片段着色器的数据
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();
fColor = vec3(1.0, 1.0, 1.0); // 指定绘制这个顶点时片段颜色
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:顶部
EmitVertex();
EndPrimitive(); // 根据当前加入的所有顶点,绘制输出类型的图元,这里是一个三角形带。
}
void main() {
//fColor = gs_in[0].color;
fColor = color[0];
build_house(gl_in[0].gl_Position);
}
片段着色器代码如下:
#version 330 core
in vec3 fColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(fColor, 1.0);
}