A geometry shader takes as input a set of vertices that form a single primitive e. The geometry shader can then transform these vertices as it sees fit before sending them to the next shader stage. What makes the geometry shader interesting is that it is able to convert the original primitive set of vertices to completely different primitives, possibly generating more vertices than were initially given.
At the start of a geometry shader we need to declare the type of primitive input we're receiving from the vertex shader. We do this by declaring a layout specifier in front of the in keyword. This input layout qualifier can take any of the following primitive values:. These are almost all the rendering primitives we're able to give to rendering calls like glDrawArrays.
The number within the parenthesis represents the minimal number of vertices a single primitive contains. We also need to specify a primitive type that the geometry shader will output and we do this via a layout specifier in front of the out keyword. Like the input layout qualifier, the output layout qualifier can take several primitive values:. With just these 3 output specifiers we can create almost any shape we want from the input primitives.
The geometry shader also expects us to set a maximum number of vertices it outputs if you exceed this number, OpenGL won't draw the extra vertices which we can also do within the layout qualifier of the out keyword. To generate meaningful results we need some way to retrieve the output from the previous shader stage.
Note that it is declared as an array, because most render primitives contain more than 1 vertex. The geometry shader receives all vertices of a primitive as its input. Using the vertex data from the vertex shader stage we can generate new data with 2 geometry shader functions called EmitVertex and EndPrimitive.
In our case we want to at least generate one line strip primitive. Whenever EndPrimitive is called, all emitted vertices for this primitive are combined into the specified output render primitive. By repeatedly calling EndPrimitive , after one or more EmitVertex calls, multiple primitives can be generated.
This particular case emits two vertices that were translated by a small offset from the original vertex position and then calls EndPrimitive , combining the two vertices into a single line strip of 2 vertices. Now that you sort of know how geometry shaders work you can probably guess what this geometry shader does.
This geometry shader takes a point primitive as its input and creates a horizontal line primitive with the input point at its center. If we were to render this it looks something like this:. Not very impressive yet, but it's interesting to consider that this output was generated using just the following render call:. While this is a relatively simple example, it does show you how we can use geometry shaders to dynamically generate new shapes on the fly.
Later in this chapter we'll discuss a few interesting effects that we can create using geometry shaders, but for now we're going to start with a simple example. To demonstrate the use of a geometry shader we're going to render a really simple scene where we draw 4 points on the z-plane in normalized device coordinates. The coordinates of the points are:. The vertex shader needs to draw the points on the z-plane so we'll create a basic vertex shader:. And we'll output the color green for all points which we code directly in the fragment shader:.
But didn't we already learn to do all this? Yes, and now we're going to spice this little scene up by adding geometry shader magic to the scene. For learning purposes we're first going to create what is called a pass-through geometry shader that takes a point primitive as its input and passes it to the next shader unmodified:.
By now this geometry shader should be fairly easy to understand. It simply emits the unmodified vertex position it received as input and generates a point primitive. The shader compilation code is the same as the vertex and fragment shaders. Be sure to check for compile or linking errors! It's exactly the same as without the geometry shader! It's a bit dull, I'll admit that, but the fact that we were still able to draw the points means that the geometry shader works, so now it's time for the more funky stuff!
Drawing points and lines isn't that interesting so we're going to get a little creative by using the geometry shader to draw a house for us at the location of each point. A triangle strip in OpenGL is a more efficient way to draw triangles with fewer vertices.
After the first triangle is drawn, each subsequent vertex generates another triangle next to the first triangle: every 3 adjacent vertices will form a triangle. If we have a total of 6 vertices that form a triangle strip we'd get the following triangles: 1,2,3 , 2,3,4 , 3,4,5 and 4,5,6 ; forming a total of 4 triangles.
Facing only matters for triangle primitive rasterization. All non-triangle primitive types are considered to rasterize the front face, and face culling only works on triangles. A quad is a 4 vertex quadrilateral primitive. The four vertices are expected to be coplanar; failure to do so can lead to undefined results. A quad is typically rasterized as a pair of triangles.
This is not defined by the GL spec, but it is allowed by it. These are special primitives that are expected to be used specifically with Geometry Shaders GS. These primitives give the geometry shader more vertices to work with for each input primitive. Normally, when using any of the above primitives, the GS gets merely one of the base type. These special primitive modes allow the GS to access vertex data for adjacent triangles. It is a primitive with a user-defined number of vertices, which is then tessellated based on the control and evaluation shaders into regular points, lines, or triangles, depending on the TES's settings.
So if you want strip-like behavior, you will need to use indices. As with other primitive types that take multiple values from the stream, incomplete patches are ignored. One of the vertices in an output primitive is designated the "provoking vertex". This vertex has special meanings for the primitive. For example, when using flat-shading on output variables , only outputs from the provoking vertex are used; every fragment generated by that primitive gets it's input from the output of the provoking vertex.
Each primitive type defines which index in the vertex stream defines the provoking vertex for a particular output primitive. There is a way to change the provoking vertex convention primarily for D3D Compatibility :.
This table defines which vertex index using one-based indices is the provoking vertex for a particular primitive type. Thus, when using last vertex conventions, the provoking vertex of primitive index 2 will be the vertex index 4 the zero-based index for the vertex is 3. This process transforms path primitives into other kinds of primitives, so by the time the rasterizer sees it, it will not be a patch anymore.
So using tessellation with flat interpolation is a dubious prospect if different primitives need different values. Download Microsoft Edge More info. Contents Exit focus mode. The following illustration depicts a rendered triangle strip.
The following code shows how to create vertices for this triangle strip. Yes No. Any additional feedback? Skip Submit. Is this page helpful?
0コメント