在顶点和片段着色器之间有一个可选的着色器阶段,称为几何着色器。 几何着色器将形成primitive (set of vertices) 单个图元的一组顶点作为输入,例如 一个点或一个三角形。 然后几何着色器可以在将这些顶点发送到下一个着色器阶段之前,按照它认为合适的方式转换这些顶点。 几何着色器的有趣之处在于它能够将原始图元(顶点集)转换为完全不同的图元,可能生成比最初给出的更多的顶点。
基础知识:
1.输入说明:
在几何着色器开始时,我们需要声明我们从顶点着色器接收到的primitive (set of vertices) 图元输入的类型。 我们通过在 in 关键字前声明一个layout specifier 布局说明符来做到这一点。 此输入布局限定符可以采用以下任何原始值:
#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();
}
points
: when drawing GL_POINTS primitives (1
).lines
: when drawing GL_LINES or GL_LINE_STRIP (2
).lines_adjacency
: GL_LINES_ADJACENCY or GL_LINE_STRIP_ADJACENCY (4
).triangles
: GL_TRIANGLES, GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN (3
).triangles_adjacency
: GL_TRIANGLES_ADJACENCY or GL_TRIANGLE_STRIP_ADJACENCY (6
).
如果我们选择将顶点绘制为 GL_TRIANGLES,我们应该将输入限定符设置为三角形。 括号内的数字表示单个图元包含的最小顶点数。
2.输出说明:
我们还需要指定几何着色器将输出的基本类型,我们通过 out 关键字前面的布局说明符来完成此操作。 与输入布局限定符一样,输出布局限定符可以采用几个原始值:
points
line_strip
triangle_strip
line_strip 输出两个顶点
triangle_strip
输出三个顶点
2.1 line strip:(线条)
线条将一组点绑定在一起,在它们之间形成一条连续的线,最少有 2 个点。 每个额外的点都会在新点和前一个点之间产生一条新线
2.2 批量检索输出:
//定义了一个interface block的输入数组
in gl_Vertex
{
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
} gl_in[];
2.3 生成新的顶点数据:
我们可以使用称为 EmitVertex 和 EndPrimitive 的 2 个几何着色器函数生成新数据
#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();
}
代码中,每次我们调用 EmitVertex 时,当前设置为 gl_Position 的向量都会添加到输出基元中。 每当调用 EndPrimitive 时,此图元的所有发射顶点都将组合到指定的输出渲染图元中。 通过在一次或多次 EmitVertex 调用后,反复调用 EndPrimitive,可以生成多个图元。
代码中,发出两个顶点,这些顶点从原始顶点位置平移了一个小的偏移量,然后调用 EndPrimitive,将两个顶点组合成一个由 2 个顶点组成的线带。
glDrawArrays(GL_POINTS, 0, 4);
2.4.Using geometry shaders (使用几何着色器)
先创建四个绿点
shader.use();
glBindVertexArray(VAO);
glDrawArrays(GL_POINTS, 0, 4);
A triangle strip needs at least 3 vertices and will generate N-2 triangles(一个三角形需要3个顶点,N个顶点渲染出N-2个三角形)
代码:(将会由点123,234,345组合渲染出3个三角形)
#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:bottom-left
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:bottom-right
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:top-left
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:top-right
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:top
EmitVertex();
EndPrimitive();
}
void main() {
build_house(gl_in[0].gl_Position);
}
配置颜色的方法:
float points[] = {
// 这三列数据代表颜色信息
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // top-left
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // top-right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // bottom-right
-0.5f, -0.5f, 1.0f, 1.0f, 0.0f // bottom-left
};
//VS代码:
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColor;
out VS_OUT {
vec3 color;
} vs_out;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
vs_out.color = aColor;
}
//geometry shader中的代码
in VS_OUT {
vec3 color;
} gs_in[];
当发射一个顶点时,该顶点将在 fColor 中存储最后存储的值作为该顶点的输出值。
fColor = gs_in[0].color; // gs_in[0] since there's only one input vertex
gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:bottom-left
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:bottom-right
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:top-left
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:top-right
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:top
EmitVertex();
EndPrimitive();
3.Exploding objects(物体炸了的效果)
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
//因为传进来的是模型,所以只能是三角形的三个顶点,顶点内部数据是贴图UV坐标
in VS_OUT {
vec2 texCoords;
} gs_in[];
out vec2 TexCoords;
uniform float time;
//三个顶点都向法线方向移动
vec4 explode(vec4 position, vec3 normal)
{
float magnitude = 2.0;
vec3 direction = normal * ((sin(time) + 1.0) / 2.0) * magnitude;
return position + vec4(direction, 0.0);
}
//根据三角形的三个顶点,用叉乘算出法线向量
vec3 GetNormal()
{
vec3 a = vec3(gl_in[0].gl_Position) - vec3(gl_in[1].gl_Position);
vec3 b = vec3(gl_in[2].gl_Position) - vec3(gl_in[1].gl_Position);
return normalize(cross(a, b));
}
void main() {
vec3 normal = GetNormal();
//绘制三个点组成的一个三角形,朝法线方向平移版本的三角形
gl_Position = explode(gl_in[0].gl_Position, normal);
TexCoords = gs_in[0].texCoords;
EmitVertex();
gl_Position = explode(gl_in[1].gl_Position, normal);
TexCoords = gs_in[1].texCoords;
EmitVertex();
gl_Position = explode(gl_in[2].gl_Position, normal);
TexCoords = gs_in[2].texCoords;
EmitVertex();
EndPrimitive();
}
4.Visualizing normal vectors(显示法线向量)
先普通渲染模型,过程略.......
再进行法线线条的渲染:
#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices = 6) out;
//模型的三角形变换成的3个顶点数据,其中内容是uv法线贴图坐标
in VS_OUT {
vec3 normal;
} gs_in[];
const float MAGNITUDE = 0.4;
uniform mat4 projection;
//对每个顶点都绘制一个可视化的法线线段
void GenerateLine(int index)
{
gl_Position = projection * gl_in[index].gl_Position;
EmitVertex();
gl_Position = projection * (gl_in[index].gl_Position +
vec4(gs_in[index].normal, 0.0) * MAGNITUDE);
EmitVertex();
EndPrimitive();
}
void main()
{
GenerateLine(0); // first vertex normal
GenerateLine(1); // second vertex normal
GenerateLine(2); // third vertex normal
}