learnOpenGL 4.9 几何着色器

263 阅读2分钟

在顶点着色器和片元着色器之间还有一个可选的几何着色器。
几何着色器的一大特点,是它可以输入和输出图元(点,线,三角形)。

以下是具体可以输入的类型
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

GLSL还提供了一个内建变量:

in gl_Vertex
{
    vec4  gl_Position;
    float gl_PointSize;
    float gl_ClipDistance[];
} gl_in[];

注意,不要把这个内建变量和我们自定义的接口块混淆。

例1 画房子

#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);
}

输入一个点,将他变换5次位置,分别发射(emit),因为我们输出的是三角形,所以点会按照顺序(123,234…)自动组成三角形输出。最后EndPrimtive结束绘制。

注意,这里虽然最终绘制的是三角形,但是在渲染循环中要写glDrawArrays(GL_POINTS, 0, 4),因为输入的图元是点。

例2 法线可视化

#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); // 第三个顶点法线
}

输入三角形,顶点沿法线方向前进一段距离,输出线条,这里我们把三角形三个顶点的法线都画出来。

注意,和上面一样,这里虽然输出的是线条,在渲染循环中却要写成glDrawArrays(GL_TRIANGLES, 0, 36);因为输入的三角形。
另外,因为这个shader只绘制法线,不会绘制原来的模型,所以要独立创建一个shader。