目录
简述:
几何着色器在顶点着色器(以及曲面细分着色器)之后,专门处理场景里的几何图形,可以将创建或销毁几何图元,可以根据顶点的信息批量处理几何图形,对顶点附近的数据进行函数的处理,快速创造出新的多边形(Vertex Shader是专门处理多边形顶点的,而Geometry shader就是专门用来处理场景中的几何图形)
输入输出:
输入:
图元—顶点,边,三角形等
points:GL_POINTS (1)
lines:GL_LINES, GL_LINE_STRIP, GL_LINE_LIST (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
几何着色器的样例解析:
样例代码:
#version330 core
//表明输入的数据,并且类型是points
layout (points) in;
//表明输出的数据,并且类型是线段,允许的最大顶点数为2
layout (line_strip, max_vertices =2) out;
void main() {
//gl_in是一个包含了前一个阶段的输出的结构体
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();
}
线段(Line Strip):
线段连接了一组点,形成一条连续的线,它最少要由两个点来组成。
gl_in:
GLSL提供给我们一个内建(Built-in)变量,来获取前一着色器阶段的输出,在内部看起来(可能)是这样的:
要注意的是,gl_Position其实应该被声明为一个数组,因为大多数的渲染图元包含多于1个的顶点,而几何着色器的输入是一个图元的所有顶点。
EmitVertex():
生成(输出)一个顶点。每次我们调用EmitVertex时,gl_Position中的向量会被添加到图元中来。
EndPrimitive():
结束一个图元的输出。
当EndPrimitive被调用时,所有发射出的(Emitted)顶点都会合成为指定的输出渲染图元。在一个或多个EmitVertex调用之后重复调用EndPrimitive能够生成多个图元。
实例尝试:
爆破物体:
简述:
当我们说爆破一个物体时,我们并不是指要将宝贵的顶点集给炸掉,我们是要将每个三角形沿着法向量的方向移动一小段时间。效果就是,整个物体看起来像是沿着每个三角形的法线向量爆炸一样。
获取法向量:
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));
}
爆破:
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);
}
它使用法向量和顶点位置向量作为参数。这个函数会返回一个新的向量,它是位置向量沿着法线向量进行位移之后的结果
当前完整的几何着色器代码:
#version330 core
layout (triangles) in;
layout (triangle_strip, max_vertices =3) out;
in VS_OUT {
vec2 texCoords;
} gs_in[];
outvec2 TexCoords;
uniformfloat 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);
returnnormalize(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();
}
法线可视化:
简述:
我们首先不使用几何着色器正常绘制场景。然后再次绘制场景,但这次只显示通过几何着色器生成法向量。几何着色器接收一个三角形图元,并沿着法向量生成三条线——每个顶点一个法向量。伪代码看起来会像是这样:
顶点着色器:
为了使用导入模型的法线,我们在将法线变换到裁剪空间坐标之前,先使用法线矩阵变换一次(几何着色器接受的位置向量是剪裁空间坐标,所以我们应该将法向量变换到相同的空间中)
#version330 core
layout (location =0) invec3 aPos;
layout (location =1) invec3 aNormal;
out VS_OUT {
vec3 normal;
} vs_out;
uniformmat4 view;
uniformmat4 model;
void main()
{
mat3 normalMatrix =mat3(transpose(inverse(view * model)));
vs_out.normal =vec3(vec4(normalMatrix * aNormal, 0.0));
gl_Position = view * model *vec4(aPos, 1.0);
}
几何着色器:
这个很好理解,就在顶点处根据法线延长,得一个点,这样就能根据顶点和延长点画出一根线。
#version330 core
layout (triangles) in;
layout (line_strip, max_vertices =6) out;
in VS_OUT {
vec3 normal;
} gs_in[];
constfloat MAGNITUDE =0.002;
uniformmat4 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
}
像素着色器:
#version330 core
outvec4 FragColor;
void main()
{
FragColor =vec4(1.0, 1.0, 0.0, 1.0);
}