实践项目:https://github.com/JackieLong/OpenGL/tree/main/project_geometry_shader_test

在顶点着色器和片段着色器之间有一个可选的几何着色器。输入是一个图元的一组顶点(三角形的三个顶点),它能这组顶点变换为不同的图元,还可以生成更多的顶点。
点击查看【processon】
一个简单的几何着色器例子如下:

  1. #version 330 core
  2. // 输入的图元类型,不同类型也指定了图元包含的最少顶点数
  3. layout (points) in; // glDrawArrays(GL_POINTS, ...)时,输入的图元顶点,以下同理。
  4. // 最少顶点数1。
  5. layout (lines) in; // GL_LINES、GL_LINE_STRIP,最少顶点数2。
  6. layout (lines_adjacency) in; // GL_LINES_ADJACENCY、GL_LINE_STRIP_ADJACENCY,最少顶点数4。
  7. layout (triangles) in; // GL_TRIANGLES、GL_TRIANGLES_STRIP、GL_TRIANGLES_FAN,最少顶点数3。
  8. layout (triangles_adjacency) in; // GL_TRIANGLES_ADJACENCY、GL_TRIANGLE_STRIP_ADJACENCY,最少顶点数1。
  9. // 输出图元类型,max_vertices表示最大输出顶点数量,不会绘制超出的顶点。下面的数字仅仅是例子。
  10. layout (points, max_vertices = 2) out;
  11. layout (line_strip, max_vertices = 4) out;
  12. layout (triangle_strip, max_vertices = 3) out;
  13. // ******************************
  14. // 这是前一着色阶段的输出
  15. in gl_Vertex
  16. {
  17. vec4 gl_Position;
  18. float gl_PointSize;
  19. float gl_ClipDistance[];
  20. } gl_in[]; // 数组,一般图元都包含多于1个顶点
  21. // ******************************
  22. void main() {
  23. gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0);
  24. EmitVertex(); // 发射一个顶点
  25. gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);
  26. EmitVertex(); // 再发射一个顶点
  27. EndPrimitive(); // 将发射的顶点按上面的输出类型,组成相应图元
  28. }

简单示例

这里展示一个顶点着色器、几何着色器、片段着色器三者联合使用的例子,绘制指令如下:

  1. //顶点数据:4个顶点,组成三个三角形,画出一个最简单的房子。
  2. float vertices_points[] =
  3. {
  4. -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // 左上
  5. 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // 右上
  6. 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 右下
  7. -0.5f, -0.5f, 1.0f, 1.0f, 0.0f // 左下
  8. };
  9. ......
  10. while(!glfwWindowShouldClose()){
  11. ......
  12. shader.use();
  13. glBindVertexArray( VAO_points );
  14. glDrawArrays( GL_POINTS, 0, 4 ); // 简单的绘制4个点。
  15. ......
  16. }
  17. ......

输出结果如下,虽然绘制指令是绘制4个点,但是最终输出远不止4个顶点,而且还是三角形图元。
OpenGL_几何着色器(Geometry Shader) - 图1
顶点着色器代码如下:

  1. #version 330 core
  2. layout (location = 0) in vec2 aPos;
  3. layout (location = 1) in vec3 aColor;
  4. // 数据传递方式一:以接口块的方式传递数据到下一个着色器阶段(几何着色器)
  5. out VS_OUT{
  6. vec3 color;
  7. } vs_out;
  8. // 数据传递方式二:
  9. out vec3 color;
  10. void main()
  11. {
  12. vs_out.color = aColor;
  13. color = aColor;
  14. gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
  15. }

几何着色器代码如下:

  1. #version 330 core
  2. // *******************************
  3. // 一、从上一个着色器阶段接收的输入数据
  4. // *******************************
  5. in VS_OUT{
  6. vec3 color;
  7. } gs_in[]; // 注意都是数组,因为几何着色器接收的是组成一个图元的顶点数据,哪怕只有一个顶点,也是在数组中
  8. in vec3 color[];
  9. // *******************************
  10. // 二、指定接收的输入图元类型
  11. // *******************************
  12. layout(points) in; // 几何着色器的输入是点图元。
  13. // *******************************
  14. // 三、指定接收的输入图元类型
  15. // *******************************
  16. layout(triangle_strip, max_vertices = 5) out; // 几何着色器的输出图元是5个点构成的三角形带(就是一个简单房子形状)
  17. // *******************************
  18. // 四、指定输出到下以着色器阶段的数据
  19. // *******************************
  20. out vec3 fColor; // 输出到片段着色器的数据
  21. void build_house(vec4 position)
  22. {
  23. gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:左下
  24. EmitVertex(); // 发射顶点,顶点将加入构建输出图元
  25. gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:右下
  26. EmitVertex();
  27. gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:左上
  28. EmitVertex();
  29. gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:右上
  30. EmitVertex();
  31. fColor = vec3(1.0, 1.0, 1.0); // 指定绘制这个顶点时片段颜色
  32. gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:顶部
  33. EmitVertex();
  34. EndPrimitive(); // 根据当前加入的所有顶点,绘制输出类型的图元,这里是一个三角形带。
  35. }
  36. void main() {
  37. //fColor = gs_in[0].color;
  38. fColor = color[0];
  39. build_house(gl_in[0].gl_Position);
  40. }

片段着色器代码如下:

  1. #version 330 core
  2. in vec3 fColor;
  3. out vec4 FragColor;
  4. void main()
  5. {
  6. FragColor = vec4(fColor, 1.0);
  7. }