【OpenGL4.0】GLSL-几何着色器详解和实例(GS:Geometry Shader)
一、什么是几何着色器(GS:Geometry Shader)
Input Assembler(IA)从顶点缓冲区上的输入流中接收顶点数据,并且把数据项转换为规范的格式。vertex
shader通常用来把顶点从模型空间变换到平面空间,vertex shader读取一个顶点,输出一个顶点。Pixel
Shader读取单一pixel属性,输出包含颜色和Z信息的的片断。而geometry
shader是DirectX10提出的,把同一区域的所有顶点作为输入,产生新的顶点或者区域。此外数据流输出(steam
output)把geometry shader输出的顶点信息复制为4个连续的输出缓冲子集。理论上来说,steam
output的输出能力和Input Assembler的输入能力相匹配。
Shader就是一段可以改变像素、顶点和几何学特征的小程序。Vertex Shader是专门处理多边形顶点的。那么Geometry
shader就是专门用来处理场景中的几何图形。在过去Vertex
Shader每一次运行只能处理一个顶点的数据,并且每次只能输出一个顶点的结果。在整个游戏场景中,绘制的几何图形的任务量非常庞大,如果仅仅依靠
Vertex Shader单一来完成,效率会极其低下。
现在DX10的设计师们在顶点与像素的处理过程中又加入了(Geometry
shader)几何着色器。它可以根据顶点的信息来批量处理几何图形,对Vertex附近的数据进行函数处理,快速创造出新的多边形。通过stream
out将这些结果传递给其他Shader或buffer,将CPU从复杂庞大的几何运算中解放出来。大爆炸,粒子效果,瀑布流水等复杂又关联的场景都可以
用Geometry shader很逼真的表现出来。
图一是渲染管线示意图。
图一 渲染管线示意图
GS位于VS与PS之间,可以完成许多模型层面上的工作诸如LOD。以往这些工作都是在CPU上完成的,占用了宝贵的CPU循环 —— CPU可是很繁忙的东西,游戏逻辑、音乐、输入接受都是靠它,却无法提高多少性能,CPU的并行计算性能是远远无法和GPU相比的。
二、几何着色器功能简介
GS被设计针对每个图元执行一次。它能够访问图元的所有顶点,以及与其相关的输入变量的值。换句话说,如果前一阶段提供了某一个输出变量,那么GS就可以访问图元中所有顶点的那个变量的值。因此,GS中的输入变量都是数组类型。
需要注意的是,GS所接受的图元和以前的不同,它只接受“可调整的”图元。
可调整的图元(Adjacency Primitive)
在OpenGL中,我们定义了新的图元类型:
GL_LINES_ADJACENCY_EXT
GL_LINE_STRIP_ADJACENCY_EXT
GL_TRIANGLES_ADJACENCY_EXT
GL_TRIANGLE_STRIP_ADJECENCY_EXT
我们可以在glBegin()、glDrawElements()中将他们作为新的参数使用。下面分别说明这4中类型的特点。
(1)Lines with Adjacecy
4*N个顶点被提供,N是要绘制的线段的数目。线段在#1个#2之间绘制,#0和#3提供调整信息。
(2)Line Strip with Adjacency
顶点数目是N+3,N是要绘制的线段的数目。线段在#1、#2、。。。、#N+1之间绘制,#0和#N+2提供调整信息。
(3)Triangles with Adjacency
顶点数目是6*N,组成三角形的是#0、#2、#4.而#1、#3、#5定义了修正三角形。
(4)Triangle Strip with Adjacency
顶点数目是2N+4.#0、#2、。。。定义三角形序列。其他的定义调整三角形。
在着色器链接之前,必须调用glProgramParameter进行相应的设置。
(1)设置GS可以输出的最大顶点数目
glProgramParameteriEXT( progname, GL_GEOMETRY_VERTICES_OUT_EXT, int value )
(2)设置GS接收的图元的类型
glProgramParameteriEXT( progname, GL_GEOMETRY_INPUT_TYPE_EXT, int value )
需要注意的是,GS的输出图元的类型必须和输入图元的类型匹配。
value的可能取值是:
A、GL_POINTS
B、GL_LINES
对应的输入图元类型可以是:GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP
C、GL_LINES_ADJACENCY_EXT
对应的输入图元类型可以是:GL_LINES_ADJACENCY_EXT、GL_LINES_STRIP_ADJACENCY_EXT
D、GL_TRIANGLES
对应的输入图元类型可以是:GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
E、GL_TRIANGLE_ADJACENCY_EXT
对应的输入图元类型可以是:GL_TRIANGLES_ADJACENCY_EXT、GL_TRIANGLE_STRIP_ADJACENCY_EXT
(3)设置输出图元类型
glProgramParameteriEXT( progname, GL_GEOMETRY_OUTPUT_TYPE_EXT, int value )
value的可能取值是:
GL_POINTS
GL_LINE_STRIP
GL_TRIANGLE_STRIP
GS可以输出0个、1个或者多个图元。这些图元的类型和它从前一阶段接收到的图元的类型不一定相同。然而,GS不能输出多种类型的图元。比如,GS可以接收三角形,输出多个线段序列,或者是输出0个或多个三角形序列。
这就是的GS有多种用途。下面是几个典型例子。一个GS可以依据某些准则比如可见性来移除一些图元。GS也可以产生额外的图元扩大所渲染的对象的形状。GS也可以计算图元的额外信息,而把图元原封不动的输出。GS也可以输出和输入图元完全不同的图元。
GLSL中GS与VS的交互
在GS中,绿色正方形所表示的是由变量gl_VerticesIn所指定的,即最大维数。
GS内置输出变量:
varying out vec4 gl_FrontColor;
varying out vec4 gl_BackColor;
varying out vec4 gl_FrontSecondaryColor;
varying out vec4 gl_BackSecondaryColor;
varying out vec4 gl_TexCoord[]; // at most gl_MaxTextureCoords
varying out float gl_FogFragCoord;
GS内置输入变量:
varying in vec4 gl_FrontColorIn[gl_VerticesIn];
varying in vec4 gl_BackColorIn[gl_VerticesIn];
varying in vec4 gl_FrontSecondaryColorIn[gl_VerticesIn];
varying in vec4 gl_BackSecondaryColorIn[gl_VerticesIn];
varying in vec4 gl_TexCoordIn[gl_VerticesIn][]; // at most will be// gl_MaxTextureCoords
varying in float gl_FogFragCoordIn[gl_VerticesIn];
varying in vec4 gl_PositionIn[gl_VerticesIn];
varying in float gl_PointSizeIn[gl_VerticesIn];
varying in vec4 gl_ClipVertexIn[gl_VerticesIn];
GS的功能实现基于两个很重要的内置函数:EmitVertex和EndPrimitive.这两个函数使得GS能够传送多个顶点或者图元到管线的
下一阶段。GS为一个顶点定义输出变量,然后调用EmitVertex。之后,GS就能够接着定义下一个顶点的相关输出变量,再次调用
EmitVertex。在对图元的所有顶点完成相同的操作之后,GS调用EndPrimitive让OpenGL知道图元的所有顶点都传送完毕。如果没有
显式调用EndPrimitive,系统会隐式调用。不过显式调用是一个好的习惯,建议这么做。如果GS没有调用EmitVertex,那么输入的图元就
不会被渲染。
需要注意的是,GS保证输出由输入图元产生的结果的顺序和输入图元的顺序是相同的。这会造成性能上的影响。比如,多个着色器单元并行运算,结果必须
被保存然后进行排序。为了在兼容性和效率之间平衡,Shader Model4.0做了一个限制:每一次执行最多只能产生1024个32位的值。
三、几何着色器运用实例
下面介绍一个实例:用GS实现一个粒子系统,绘制了很多上面贴了纹理的小正方形。
效果如下:
顶点着色器:
#version 400 layout (location = 0) in vec3 VertexPosition; uniform mat4 ModelViewMatrix; uniform mat3 NormalMatrix; uniform mat4 ProjectionMatrix; void main() { gl_Position = ModelViewMatrix * vec4(VertexPosition,1.0); }
几何着色器:
#version 400 layout( points ) in; layout( triangle_strip, max_vertices = 4 ) out; uniform float Size2; // Half the width of the quad uniform mat4 ProjectionMatrix; out vec2 TexCoord; void main() { mat4 m = ProjectionMatrix; gl_Position = m * (vec4(-Size2,-Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(0.0,0.0); EmitVertex(); gl_Position = m * (vec4(Size2,-Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(1.0,0.0); EmitVertex(); gl_Position = m * (vec4(-Size2,Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(0.0,1.0); EmitVertex(); gl_Position = m * (vec4(Size2,Size2,0.0,0.0) + gl_in[0].gl_Position); TexCoord = vec2(1.0,1.0); EmitVertex(); EndPrimitive(); }
片断着色器:
#version 400 in vec2 TexCoord; uniform sampler2D SpriteTex; layout( location = 0 ) out vec4 FragColor; void main() { FragColor = texture(SpriteTex, TexCoord); }
GLSL-几何着色器详解跟实例(GS:Geometry Shader)[转]