Geometry shader – receives as its input complete primitives as a collection of vertices, and these inputs are represented as array (Geometry shader接收完整图形的顶点集合,这些顶点集合在geometry shader中通过gl_in[]数组的方式访问)
gl_in的声明:
in gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
}gl_in[];
Each invocation of the geometry shader processes an entire primitive, it is possible to implement techniques that require access to all of the vertices of that primitives (因为geometry shader是针对一个完整图形的顶点集合来做处理,所以geometry shader更适合用于处理针对图形程度上的处理 -- 比如对传入的vertex进行处理生成多个primitive(叫做amplification(扩大)))
Geometry Shader的使用:
Before a geometry shader may be linked, the input primitive type, output primitive type, and the maximum number of vertices that is might produce must be specified (在链接geometry shader之前,我们必须先定义geometry shader的输入输出类型)
e.g:
layout (input primitive type) in;
layout (output primitive type, max_vertices = number) out; (这里的max_vertices会遇到一个硬件限制所支持的max_vertices的最大值--超出最大值后program link会出错,通过在program link后调用glGetProgramiv() with GL_INFO_LOG_LENGTH parameter可以得program link的出错信息,shader compile的log同理)
EmitVertex() – produces a new vertex at the output of the geometry shader. Each time it is called, a vertex is appended to the end of the current strip (将新的vertex加入到primitive的队列)
EndPrimitive() – breaks the current strip and signals OpenGL that a new strip should be started the next time EmitVertex() is called (将之前所加入的vertex算作一个primitive的信息,通知OpenGL开始下一个primitive的构造)
Note:
When the geometry shader exits, the current primitive is ended implicitly (如果geometry shader结束了,那么当前还没有调用EndPrimitive()的primitive将视作结束)
When EndPrimitive() is called, any incomplete primitives will simply be discarded (当EndPrimitive()被调用的时候,数据不完全的primitive将被抛弃 -- 不调用这个方法的primitive相当于culling掉)
Interface block helps to group all the data for a single vertex, rather than managing collections of arrays (Interface block 可以帮助我们把相关的一系列数据放到一个结构体中来通过数组的形式访问)
e.g:
out VS_GS_INTERFACE
{
out vec4 position;
out vec3 normal;
out vec4 color;
out vec2 tex_coord[4];
}vs_out;
in VS_GS_INTERFACE
{
out vec4 position;
out vec3 normal;
out vec4 color;
out vec2 tex_coord[4];
}gs_in[];
Build-in variables: (这里的build in variables是为了在接下来的shader中知道geometry shader里自定义的一些输出(比如接收到的primitive是输出的第几个primitive等信息))
gl_PrimitiveID -- fragment shader uses to identify the primitive to which the fragment belongs (gl_PrimitiveIDIn这个是geometry shader的输出,通过使用gl_PrimitiveIDIn我们可以实现只生成特定的primitive)
gl_Layer & gl_ViewPortIndex -- both are used in layered rendering and with viewport arrays
Version 4.3 and above GLSL support two-dimensional arrays (OpenGL 4.3以后GLSL支持二位数组)
Special Geometry Shader primitives:
Adjacency primitive types:
lines_adjacency – GL_LINE_ADJACENCY or GL_LINE_STRIP_ADJACENCY
triangles_adjacency – GL_TRIANGLE_LINES_ADJACENCY or GL_TRIANGLE_STRIP_ADJACENCY
Geometry Shader Outputs:
The same gl_PerVertex interface block specification is used for per-vertex outputs in the geometry shader (gl_PerVertex用于geometry shader针对每一个vertex的输出)
Although the same gl_PerVertex interface block is used to declare the geometry shader outputs, in this instance it has no name, and so the outputs are essentially in global scope
Each geometry shader invocation can produce multiple output vertices; each vertex must be explicitly produced and used to form a new vertex
flat key words – glProvokingVertex() specify which vertex is to be used as the provoking vertex (只有被定义成provoking的vertex会在接下来的shader中被使用)
Producing Primitives:
Another unique feature of geometry shader is that they can have a different primitive type for their output than they do for their input. (EG: wireframe rendering(以下是个人的思考:网格渲染可以通过把传入的三角形输出成Line的类型来实现网格效果), billboards and even interesting instancing effects)
Culling Geometry:
Selective culling (geometry shader通过对特定的gl_PrimitiveIDIn进行生成特定的primitive实现selective culling)
Geometry Amplification:
Amplification – produces more primitives on its output than it accepts on its input
(can be used to implement fur shells or moderate tessellation -- 因为可以对传入的primitive数据进行处理并生成多个primitive,所以能通过复制并改变primitive的信息数据来实现毛发等效果)
Gl_MaxGeometryOutputVertices & glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES)
Fur Rendering Using a Geometry Shader:
The basic principle is that hair or fur on a surface is modeled as a volume that is rendered using slices, and the geometry shader is used to generate those slices
Advanced Transform Feedback:
Multiple Output Streams:
Multiple streams of vertices can be declared as outputs in the geometry shader (通过stream我们可以把一些额外需要保存的信息保存到特定的stream里便于transform feedback buffer去访问并进行进一步的处理)
Using the stream layout qualifier – this layout qualifier may be applied globally, to an interface block, or to a single output declaration
Each stream is numbered, starting from zero, max number of streams – GL_MAX_VERTEX_STREAMS
When the stream number is given at global scope, all subsequently declared geometry shader outputs become members of that stream until another output stream layout qualifier is specified
Multiple output stream’s built in GLSL functions:
EmitStreamVertex(int stream)
EndStreamVertex(int stream)
glTransformFeedbackVaryings() – tell OpenGL how those streams are mapped into transform feedback buffer (告诉OpenGL各个stream是怎么映射到transform feedback buffer的)
When multiple streams are active, it is required that variables associated with a single stream are not written into the same buffer binding point as those associated with any other stream
gl_NextBuffer is used to signal that the following output variables are to be recorded into the buffer object bound to the next transform feedback binding point (gl_NexBuffer告诉OpenGL后面的数据将绑定到下一个transform feedback buffer)
if rasterization & fragment shader are enabled, the output variables belonging to stream 0 will be used to form primitives for rasterization and will be passed into the fragment shader. Output variables belonging to other streams will not be visible in the fragment shader and if transform feedback is not active, they will be discarded (这里需要注意,一旦rasterization 和 fragment shader被开启或者transform feedback没有被开启,那么geometry shader里面指定的out变量只有属于stream 0的才会被进行处理,其他都会被抛弃)
Note:
When multiple output streams are used in a geometry shader, they must all have points as the primitive type (注意,当multiple output streams被开启时,geometry shader必须指定输出类型为point,当first pass的时候geometry shader指定输出类型为point,second pass的时候geometry shader可以针对第一次transform feedback记录的point数据进行处理输出triangle等)
Primitive Queries:
Reason:
Geometry shader can emit a variable number of vertices per invocation (因为geometry shader会扩展出很多primitive和vertices,我们在访问一些跟transform feedback buffer相关的数据的时候就不那么直接 -- 这里要提一下没有geometry shader,vertex shader结合transform feeback buffer的使用是一对一的输出,而geometry shader不一样,会有一堆多的primitive,vertices的输出)
Problem:
The number of vertices recorded into transform feedback buffers when a geometry shader is present may not be easy to infer
Solution:
Two types of queries are available to count both the number of primitives the geometry shader generates, and the number of primitives actually written into the transform feedback buffers(通过Primitive Queries我们可以得知geometry shader的primitives,vertices生成数量和实际被写入transform feedback buffer的primitive,vertices数量)
GL_PRIMITIVES_GENERATED -- query counts the number of vertices output by the geometry shader – valid at any time
&
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN – query counts the number of vertices actually written into a transform feedback buffer – only valid when transform feedback is active
Due to geometry shader supports multiple transform feedback streams, primitive queries are indexed (因为geometry shader支持multiple transform feedback streams,所以primitive queries也是indexed的)
glGenQueries(***)
glBeginQueryIndexed(***)
glGetQueryObjectiv(***)
Using Transform Feedback Results:
The CPU will wait for the GPU to finish rendering anything that might contribute to the primitive count , and then the GPU will wait for the CPU to send a new rendering command using that count. – Benefits:
Avoid make the round trip from the GPU to the CPU and back again (primitive count is computed on GPU, CPU use the count causes copy count info from GPU -- 避免复制GPU上的数据到CPU上的消耗)
Functions:
glDrawTransformFeedback(***) & glDrawTransformFeedbackStream(***)
With the count parameter taken from a virtual GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query running on stream stream of the transform feedback object id.
No need to execute a real GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN query and the primitive count is never actual transferred from the GPU to the CPU
Combining Multiple Streams and DrawTransformFeedback:
Problem:
OpenGL requires that when multiple output streams are in use in a geometry shader, the output primitive type is points (so the first pass used point type)
Solution:
If we have recorded points into the transform feedback buffer, we can still draw the second and third passes using GL_TRIANGLES.
Reason:
Once the vertices have been recorded into the transform feedback buffers, they are simply interpreted as raw data and can be used for any purpose
Geometry Shader Instancing:
Geometry shader instancing – is avaible that only runs the geometry shader and subsequent stages (rasterization) multiple times, rather than the whole pipeline (Geometry shader instancing draw call是通过运行多次geometry和rasterization和fragment来实现的)
Geometry shader instancing is enabled in the shader by specifying the invocations layout qualifier
layout (triangles, invocations = 4) in; - invocations = 4 indicates that the geometry shader will be called 4 times for each input primitives
gl_InvocationID -- contain the invocation number while the geometry shader is running
GL_MAX_GEOMETRY_SHADER_INVOCATIONS – The maximum invocation count for geometry shader supported by the OpenGL
注意:
这里geometry shader instancing draw跟之前我们调用glDrawArraysInstanced是不一样的,glDrawArraysInstanced是API级别上的instance,geometry shader instancing是geometry级别上的instance
Multiple Viewports and Layered Rendering:
gl_ViewportIndex & gl_Layer (output variables available in the geometry shader that can redirect rendering into different regions of the framebuffer, or to layers of array textures. Their values are also available as input to fragment shaders)
gl_ViewportIndex is used to specify which set of viewport parameters will be used to perform the viewport transformation by OpenGL
glViewportIndexedf() or glViewportIndexedfv() – specify how window x and y coordinates are generated from clip coordinates
glDepthRangeIndexed() – specify how the window z coordinate is generated
“
The viewport origins, widths, and heights are stored in an array by
OpenGL and when a geometry shader is active that writes to
gl_ViewportIndex, that value is used to index into the array of viewport
parameters. If the geometry shader does not write to gl_ViewportIndex,
or if no geometry shader is present, the first viewport is used.
“ (Multiple viewport concept (多个视图窗口) -- 这里主要是通过gl_ViewportIndex()定义多个viewport,然后在geometry shader中通过指定primitive的输出输出viewport来实现多个视图窗口)
glScissorIndexed() & glScissorIndexedv() – specify individual scissor rectangles (Scissor是用于pixel test实现对特定范围的像素裁剪,Scissor也是通过gl_ViewPortIndex来指定哪一个Scissor用于特定像素的ownership test)
GL_MAX_VIEWPORTS – the maximum number of viewports are supported
Layered Rendering:
It is also possible to use a 2D array texture as a color attachment and render into the slices of the array using a geometry shader (传入2D的纹理数组数据当做color attachment,通过geometry shader把传入的2D纹理数组信息去渲染多个slices)
A restriction exits when using layered attachments to framebuffer: (使用layered attachment到framebuffer的规则)
All the attachments of that framebuffer must be layered (framebuffer的所有attachment都必须是layered)
Also, all attachments of a layered framebuffer must be of the same type (所有attach到layered framebuffer的attachment必须是同样类型)
gl_Layer – built in variable in geometry shader – that is used to specify the zero-based index of the layer into which rendering will be directed
e.g:
Cube-Map
1. 添加cube_map texture为color attachment到framebuffer中2. cube-map texture(2D texture)这里会被划分成六个layer的array texture
3. 通过instanced geometry shader生成六个faces(对应六个layer),通过gl_InvocationID和gl_Layer访问六个faces并做相应的projection matrices运算实现Cube_Map Face的效果
Real Effect:
Fur Example Final Effect:
MultiPle viewport final effect:
Geometry and transform feed back buffer using final effects: