This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License
Mike Bailey mjb@cs.oregonstate.edu
Computer Graphics
GLSL Geometry Shaders
geometry_shaders.pptx
mjb – January 2, 2021
1
Computer Graphics
= Fixed Function
= Programmable
Here’s What We Know So Far
2
mjb – January 2, 2021
Computer Graphics
Here’s What We Know So Far
3
One Vertex In
One Vertex Out
mjb – January 2, 2021
Computer Graphics
= Fixed Function
= Programmable
Arrays of Vertices Out, Possibly with a Change of Topology
Here’s What We Have Next
4
One Vertex In
Array of Vertices Out
mjb – January 2, 2021
Computer Graphics
The Geometry Shader: Where Does it Fit in the Pipeline?
5
= Fixed Function
= Programmable
If in use, it is always the last stop before the Rasterizer
mjb – January 2, 2021
Your application { generates these
Points, Lines, Line Strip, Line Loop,,Lines with Adjacency, Line Strip with Adjacency, Triangles, Triangle Strip, Triangle Fan, Triangles with Adjacency, Triangle Strip with Adjacency
The driver translates them { into one of these and feeds them one-at-a-time into the Geometry Shader
Point, Line, Line with Adjacency, Triangle, Triangle with Adjacency
TheGeometryShader { generates (almost) as many of these as it wants
Points, LineStrips, TriangleStrips
Computer Graphics
triangles can generate triangle strips, points can generate points, etc.
Geometry Shader: What Does it Do?
There needn’t be any correlation between Geometry Shader input type
and Geometry Shader output type. Points can generate triangles,
mjb – January 2, 2021
6
Computer Graphics
Additional Topologies that Geometry Shaders made Available:
GL_LINES_ADJACENCY
GL_LINE_STRIP_ADJACENCY GL_TRIANGLES_ADJACENCY GL_TRIANGLE_STRIP_ADJECENCY
mjb – January 2, 2021
7
Computer Graphics
Adjacency Primitives (and what they do when not using shaders)
8
This is what Fixed-Function OpenGL expects these topologies to mean. In Shader World, they can mean whatever you want them to mean. In Shader World, it’s just a way to get some number of vertices into a Geometry Shader.
Lines with Adjacency
4N vertices are given.
(where N is the number of line segments to draw).
A line segment is drawn between #1 and #2.
Vertices #0 and #3 are there to provide adjacency information.
Line Strip with Adjacency
012345
0123
N+3 vertices are given
(where N is the number of line segments to draw).
A line segment is drawn between #1 and #2, #2 and #3, …, #N and #N+1. Vertices #0 and #N+2 are there to provide adjacency information.
N=1
N=3
mjb – January 2, 2021
Computer Graphics
3
7
Adjacency Primitives (and what they do when not using shaders)
9
Triangles with Adjacency
2 13
6N vertices are given
(where N is the number of triangles to draw). 0 Points 0, 2, and 4 define the triangle.
Points 1, 3, and 5 tell where adjacent triangles are.
4 N = 1
Triangle Strip with Adjacency
1
59 2 6 10
4+2N vertices are given
(where N is the number of triangles to draw).
Points 0, 2, 4, 6, 8, 10, …define the triangles. 0 Points 1, 3, 5, 7, 9, 11, … tell where adjacent triangles are.
11
5
4
8
N=4
mjb – January 2, 2021
Computer Graphics
Adjacency Primitives (and what they do when you are using shaders)
10
In general, we will use the “with adjacency” primitives as a way of importing some number of vertices into the geometry shader.
These are the most useful:
GL_LINES_ADJACENCY GL_TRIANGLES_ADJACENCY
4 vertices 6 vertices
0123
2 13
04 5
mjb – January 2, 2021
If a Vertex Shader Writes Variables as:
then the Geometry Shader will Read Them as:
and will Write Them to the Fragment Shader as:
gl_Position gl_PointSize
gl_PositionIn[ ] gl_PointSizeIn[ ]
gl_Position gl_PointSize
“out”
“in”
“out”
What Do the Inputs to a Geometry Shader Look Like?
11
are given by the variable gl_VerticesIn, although you will already
1 GL_POINTS
2 GL_LINES
4 GL_LINES_ADJACENCY
3 GL_TRIANGLES
6 GL_TRIANGLES_ADJACENCY
In the Geometry Shader, the dimensions indicated by know this by the type of geometry you are inputting
Computer Graphics
mjb – January 2, 2021
What Do the Outputs to a Geometry Shader Look Like?
12
• gl_Position
• gl_PointSize
• Plus, any of your own variables that you have declared as out
When the Geometry Shader calls
Geometry Shader, or (2) returning from the EndPrimitive( ) call. Also, there is no need
to call EndPrimitive( ) at the end of the Geometry Shader – it is implied.
Computer Graphics
EmitVertex( )
this set of variables is copied to an entry in the shader’s Primitive Assembly step
When the Geometry Shader calls
EndPrimitive( )
the vertices that have been saved in the Primitive Assembly elements are then assembled, rasterized, etc.
Note: there is no “BeginPrimitive( )” function. It is implied by (1) the start of the
mjb – January 2, 2021
If you are using a Geometry Shader, then the GS must be used if you want to pass information from the Vertex Shader to the Fragment Shader
Computer Graphics
G
in vec4 vColor[3];
F
in vec4 gColor;
out vec4 gl_Position;
V
These are already declared for you
out vec4 vColor; vColor = gl_Color;
Primitive Assembly
in vec4 gl_PositionIn[3];
gl_Position = gl_PositionIn[0]; gColor = vColor[0]; EmitVertex( );
…
out vec4 gl_Position;
out vec4 gColor; gColor = vColor[ k ];
Primitive Assembly Rasterizer
mjb – January 2, 2021
13
Computer Graphics
P0
P3
P1
Example: A Bézier Curve
P(u)(1u)3P 3u(1u)2P3u2(1u)P u3P 0123
Need to pass 4 points in to define the curve. You need to pass N points out to draw the curve as a Line Strip.
P2
mjb – January 2, 2021
14
beziercurve.glib
beziercurve.vert
beziercurve.frag
Computer Graphics
}
}
void main( ) {
Example: Expanding 4 Points into a Bezier Curve with a Variable Number of Line Segments
Vertex beziercurve.vert
Geometry beziercurve.geom Fragment beziercurve.frag
Program BezierCurve uNum <2 4 50>
LineWidth 3.
LinesAdjacency [0. 0. 0.] [1. 1. 1.] [2. 1. 2.] [3. -1. 0.]
void main( ) {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_FragColor = vec4( 0., 1., 0., 1. );
mjb – January 2, 2021
15
beziercurve.geom
Example: Expanding 4 Points into a Bezier Curve with a Variable Number of Line Segments
#version 330 compatibility
#extension GL_EXT_gpu_shader4: enable #extension GL_EXT_geometry_shader4: enable layout( lines_adjacency ) in;
layout( line_strip, max_vertices=200 ) out; uniform int uNum;
void
main( )
{
Note: layout directives are a GLSL-ism and are used to define what the storage looks like
Computer Graphics
}
mjb – January 2, 2021
float dt = 1. / float(uNum); float t = 0.;
for( int i = 0; i <= uNum; i++ ) {
}
float omt = 1. - t;
float omt2 = omt * omt; float omt3 = omt * omt2; float t2 = t * t;
float t3 = t * t2;
vec4 xyzw =
gl_Position = xyzw; EmitVertex( )
t += dt;
omt3 * gl_PositionIn[0].xyzw + 3. * t * omt2 * gl_PositionIn[1].xyzw + 3. * t2 * omt * gl_PositionIn[2].xyzw +
t3 * gl_PositionIn[3].xyzw;
16
Computer Graphics
Example: Expanding 4 Points into a Bezier Curve with a Variable Number of Line Segments
uNum = 5 uNum = 25
mjb – January 2, 2021
17
beziercurve.vert
...
Note: It would have made no Difference if the Matrix Transform had been done in the Geometry Shader Instead
void main( ) {
}
beziercurve.geom
} }
Computer Graphics
gl_Position = gl_Vertex;
vec4 xyzw =
omt3 * gl_PositionIn[0].xyzw + 3. * t * omt2 * gl_PositionIn[1].xyzw + 3. * t2 * omt * gl_PositionIn[2].xyzw +
gl_Position = gl_ModelViewProjectionMatrix * xyzw; EmitVertex( )
t += dt;
t3 * gl_PositionIn[3].xyzw;
mjb – January 2, 2021
18
Computer Graphics
Another Example: Shrinking Triangles
19
mjb – January 2, 2021
Computer Graphics
P0
P1
Example: Shrinking Triangles
20
P2
CG=(P0 +P1 +P2 )/3.; P0’=CG+uShrink*(P0 -CG) P1’=CG+uShrink*(P1 -CG) P2’=CG+uShrink*(P2 -CG)
Centroid = “CG”
mjb – January 2, 2021
Computer Graphics
V[0] = gl_PositionIn[0].xyz; V[1] = gl_PositionIn[1].xyz; V[2] = gl_PositionIn[2].xyz; CG=( V[0]+V[1]+V[2] )/3.; ProduceVertex( 0 ); ProduceVertex( 1 ); ProduceVertex( 2 );
#version 330 compatibility
#extension GL_EXT_gpu_shader4: enable #extension GL_EXT_geometry_shader4: enable layout( triangles ) in;
layout( triangle_strip, max_vertices=200 ) out;
uniform float uShrink;
in vec3 vNormal[3];
out float gLightIntensity;
const vec3 LIGHTPOS = vec3( 0., 10., 0. ); vec3 V[3];
vec3 CG;
void
ProduceVertex( int v ) {
void main( ) {
}
mjb – January 2, 2021
shrink.geom
21
g LightIntensity = dot( normalize(LIGHTPOS- V[v]), vNormal[v] ); g LightIntensity = abs( gLightIntensity );
gl_Position = gl_ModelViewProjectionMatrix * vec4( CG + uShrink * ( V[v] - CG ), 1. );
EmitVertex( ); }
Computer Graphics
(0,0)
(1,0)
Another Example: Sphere Subdivision
It’s often useful to be able to parameterize a triangle into (s,t), like this:
t
Note! There is no place in this triangle where s = t = 1.
V2
t=0
V0 s V1
(0,1)
v(s,t) = V0 + s*(V1-V0) + t*(V2-V0)
mjb – January 2, 2021
22
V0 uLevel = 0 V1 V0 numLayers = 2level = 1
uLevel = 1 V1 V0 numLayers = 2
uLevel = 2 V1 numLayers = 4
Computer Graphics
Example: Sphere Subdivision
V2 V2 V2
mjb – January 2, 2021
23
spheresubd.glib
Computer Graphics
Triangles[0.0. 1.] [1.0. 0.] [0. 1.0.] Triangles [ 1. 0. 0.] [ 0. 0. -1.] [0. 1. 0.] Triangles [ 0. 0. -1.] [-1. 0. 0.] [0. 1. 0.] Triangles [-1. 0. 0.] [ 0. 0. 1.] [0. 1. 0.]
Triangles [ 0. 0. 1.] [ 1. 0. 0.] [0. -1. 0.] Triangles [ 1. 0. 0.] [ 0. 0. -1.] [0. -1. 0.] Triangles [ 0. 0. -1.] [-1. 0. 0.] [0. -1. 0.] Triangles [-1. 0. 0.] [ 0. 0. 1.] [0. -1. 0.]
Example: Sphere Subdivision
Vertex spheresubd.vert
Geometry spheresubd.geom
Fragment spheresubd.frag
Program SphereSubd uLevel <0 0 10> uRadius <.5 1. 5.> uColor { 1. .5 .15 1. }
mjb – January 2, 2021
24
spheresubd.vert
spheresubd.frag
Computer Graphics
void main( ) {
}
uniform vec4 uColor;
in float
gLightIntensity;
void main( ) {
}
Example: Sphere Subdivision
gl_Position = gl_Vertex;
gl_FragColor = vec4( gLightIntensity*uColor.rgb, 1. );
mjb – January 2, 2021
25
spheresubd.geom
Example: Sphere Subdivision
#version 330 compatibility
#extension GL_EXT_gpu_shader4: enable #extension GL_EXT_geometry_shader4: enable layout( triangles ) in;
layout( triangle_strip, max_vertices=200 ) out;
uniform int uLevel;
uniform float uRadius;
out float gLightIntensity;
const vec3 LIGHTPOS = vec3( 0., 10., 0. );
vec3 V0, V01, V02;
void
ProduceVertex( float s, float t ) {
}
vec3 v = V0 + s*V01 + t*V02;
v = normalize(v);
vec3 n = v;
vec3 tnorm = normalize( gl_NormalMatrix * n ); // the transformed normal
vec4 ECposition = gl_ModelViewMatrix * vec4( (uRadius*v), 1. );
gLightIntensity = abs( dot( normalize(LIGHTPOS – ECposition.xyz), tnorm ) );
gl_Position = gl_ProjectionMatrix * ECposition; EmitVertex( );
Computer Graphics
mjb – January 2, 2021
26
spheresubd.geom
void main( ) {
V01 = ( gl_PositionIn[1] – gl_PositionIn[0] ).xyz; V02 = ( gl_PositionIn[2] – gl_PositionIn[0] ).xyz; V0 = gl_PositionIn[0].xyz;
int numLayers = 1 << uLevel; float dt = 1. / float( numLayers ); float t_top = 1.;
for( int it = 0; it < numLayers; it++ ) {
Computer Graphics
.. .
Example: Sphere Subdivision
mjb – January 2, 2021
27
spheresubd.geom
Example: Sphere Subdivision
for( int it = 0; it < numLayers; it++ ) {
}
Computer Graphics
}
mjb – January 2, 2021
float t_bot = t_top - dt; float smax_top = 1. - t_top; float smax_bot = 1. - t_bot;
int nums = it + 1;
float ds_top = smax_top / float( nums - 1 ); float ds_bot = smax_bot / float( nums );
float s_top = 0.; float s_bot = 0.;
for( int is = 0; is < nums; is++ ) {
}
ProduceVertex( s_bot, t_bot ); ProduceVertex( s_top, t_top ); s_top += ds_top;
s_bot += ds_bot;
ProduceVertex( s_bot, t_bot ); EndPrimitive( );
t_top = t_bot; t_bot -= dt;
28
Level = 0
Level = 2
Computer Graphics
Example: Sphere Subdivision with One triangle
29
Level = 1
Level = 3
mjb – January 2, 2021
Level = 2
Computer Graphics
Example: Sphere Subdivision with the Whole Sphere (8 triangles)
30
Level = 0
Level = 1
Level = 3
mjb – January 2, 2021
V0
Level = 2 V1 numLayers = 4
Time
Computer Graphics
yy vt1at2 0y2y
V2
Another Example: Explosion
1. Breakthetrianglesintopoints
Time
2. Treat each point’s distance from the triangle’s CG as an initial velocity
3. Followthelawsofprojectilemotion:
xx0 vxt
Time
mjb – January 2, 2021
31
Computer Graphics
explode.geom
vec3 V0, V01, V02; vec3 CG;
void
ProduceVertex( float s, float t ) {
}
Example: Explosion
#version 330 compatibility
#extension GL_EXT_gpu_shader4: enable #extension GL_EXT_geometry_shader4: enable layout( triangles ) in;
layout( points, max_vertices=200 ) out;
uniform int uLevel; uniform float uGravity; uniform float uTime; uniform float uVelScale;
vec3 v = V0 + s*V01 + t*V02;
vec3 vel = uVelScale * ( v - CG );
v = CG + vel*uTime + 0.5*vec3(0.,uGravity,0.)*uTime*uTime; gl_Position = gl_ProjectionMatrix * vec4( v, 1. ); EmitVertex( );
mjb – January 2, 2021
32
explode.geom
Example: Explosion
Computer Graphics
void main( ) {
}
V01 = ( gl_PositionIn[1] - gl_PositionIn[0] ).xyz;
V02 = ( gl_PositionIn[2] - gl_PositionIn[0] ).xyz;
V0 = gl_PositionIn[0].xyz;
CG = ( gl_PositionIn[0].xyz + gl_PositionIn[1].xyz + gl_PositionIn[2].xyz ) / 3.;
int numLayers = 1 << uLevel;
float dt = 1. / float( numLayers ); float t = 1.;
for( int it = 0; it <= numLayers; it++ ) {
float smax = 1. - t;
int nums = it + 1;
float ds = smax / float( nums - 1 ); float s = 0.;
for( int is = 0; is < nums; is++ ) {
t -= dt; }
ProduceVertex( s, t );
s += ds; }
mjb – January 2, 2021
33
Computer Graphics
Example: Explosion
mjb – January 2, 2021
34
Computer Graphics
2 13
04 5
1. Compute the normals of each of the four triangles
Another Example: Silhouettes
2. If there is a sign difference between the z component of the center triangle’s normal and the z component of an adjacent triangle’s normal, draw their common edge
I.e., you are looking for a crease.
mjb – January 2, 2021
35
silh.glib
Computer Graphics
Obj bunny.obj
Example: Silhouettes
Vertex silh.vert
Geometry silh.geom
Fragment silh.frag
Program Silhouette uColor { 0. 1. 0. 1. }
ObjAdj bunny.obj
mjb – January 2, 2021
36
silh.vert
silh.frag
Computer Graphics
void main( ) {
}
uniform vec4 uColor;
void main( ) {
}
Example: Silhouettes
gl_Position = gl_ModelViewMatrix * gl_Vertex;
gl_FragColor = vec4( uColor.rgb, 1. );
mjb – January 2, 2021
37
silh.geom
Example: Silhouettes
#version 330 compatibility
#extension GL_EXT_gpu_shader4: enable #extension GL_EXT_geometry_shader4: enable layout( triangles_adjacency ) in;
layout( line_strip, max_vertices=200 ) out;
void main( )
{
2 13
Computer Graphics
vec3 V0 = gl_PositionIn[0].xyz; vec3 V1 = gl_PositionIn[1].xyz; vec3 V2 = gl_PositionIn[2].xyz; vec3 V3 = gl_PositionIn[3].xyz; vec3 V4 = gl_PositionIn[4].xyz; vec3 V5 = gl_PositionIn[5].xyz;
04 5
vec3 N042 = cross( V4-V0, V2-V0 ); vec3 N021 = cross( V2-V0, V1-V0 ); vec3 N243 = cross( V4-V2, V3-V2 ); vec3 N405 = cross( V0-V4, V5-V4 );
// the center triangle’s normal
if( dot( N042, N021 ) < 0. )
N021 = vec3(0.,0.,0.) - N021;
// make sure each outer triangle’s
// normal is in the same general direction
if( dot( N042, N243 ) < 0. )
N243 = vec3(0.,0.,0.) - N243;
if( dot( N042, N405 ) < 0. )
N405 = vec3(0.,0.,0.) - N405;
mjb – January 2, 2021
38
silh.geom
Example: Silhouettes
} C}omputer Graphics
mjb – January 2, 2021
if( N042.z * N021.z <= 0. ) {
}
}
gl_Position = gl_ProjectionMatrix * vec4( V0, 1. ); EmitVertex( );
gl_Position = gl_ProjectionMatrix * vec4( V2, 1. ); EmitVertex( );
2 13
EndPrimitive( );
if( N042.z * N243.z <= 0. ) {
04 5
gl_Position = gl_ProjectionMatrix * vec4( V2, 1. ); EmitVertex( );
gl_Position = gl_ProjectionMatrix * vec4( V4, 1. ); EmitVertex( );
EndPrimitive( );
if( N042.z * N405.z <= 0. ) {
gl_Position = gl_ProjectionMatrix * vec4( V4, 1. ); EmitVertex( );
gl_Position = gl_ProjectionMatrix * vec4( V0, 1. ); EmitVertex( );
EndPrimitive( );
39
Computer Graphics
Example: Bunny Silhouettes
40
mjb – January 2, 2021
Computer Graphics
Another Example: Hedgehog Plots
41
mjb – January 2, 2021
Computer Graphics
EndPrimitive( ); }
#version 330 compatibility
#extension GL_EXT_gpu_shader4: enable #extension GL_EXT_geometry_shader4: enable layout( triangles ) in;
layout( line_strip, max_vertices=200 ) out;
uniform int uDetail; uniform float uDroop; uniform int uLength; uniform float uStep; in vec3 vTnorm[3]; in vec4 vColor[3]; out vec4 gColor;
int ILength;
vec3 Norm[3]; vec3 N0, N01, N02; vec4 V0, V01, V02;
void
ProduceVertices( float s, float t ) {
vec4 v = V0 + s*V01 + t*V02;
vec3 n = normalize( N0 + s*N01 + t*N02 );
for( int i = 0; i <= uLength; i++ ) {
gl_Position = gl_ProjectionMatrix * v; gColor = vColor[0];
EmitVertex( );
v.xyz += uStep * n;
v.y -= uDroop * float(i*i); }
hedgehog.geom, I
42
mjb – January 2, 2021
Computer Graphics
void main( ) {
hedgehog.geom, II
43
V0 = gl_PositionIn[0];
V01 = ( gl_PositionIn[1] - gl_PositionIn[0] ); V02 = ( gl_PositionIn[2] - gl_PositionIn[0] ); Norm[0] = vTnorm[0];
Norm[1] = vTnorm[1];
Norm[2] = vTnorm[2];
if( dot( Norm[0], Norm[1] ) < 0. ) Norm[1] = -Norm[1];
if( dot( Norm[0], Norm[2] ) < 0. ) Norm[2] = -Norm[2];
N0 = normalize( Norm[0] );
N01 = normalize( Norm[1] - Norm[0] ); N02 = normalize( Norm[2] - Norm[0] );
int numLayers = 1 << uDetail;
mjb – January 2, 2021
Computer Graphics
}
float smax = 1. - t;
hedgehog.geom, III
44
float dt = 1. / float( numLayers ); float t = 1.;
for( int it = 0; it <= numLayers; it++ ) {
int nums = it + 1;
float ds = smax / float( nums - 1 );
float s = 0.;
for( int is = 0; is < nums; is++ ) {
ProduceVertices( s, t );
s += ds; }
t -= dt; }
mjb – January 2, 2021
Computer Graphics
Ducky Hedgehog Plot
45
mjb – January 2, 2021
Computer Graphics
Hedgehog Plots Gone Wild
46
mjb – January 2, 2021
A GLSL Built-in Variable for the Geometry Shaders
int gl_PrimitiveIDIn
• Tells the number of primitives processed since the last time glBegin( ) was called • Calling a vertex buffer drawing function counts as an implied glBegin( )
• gl_PrimitiveIDIn is 0 for the first primitive after the glBegin( )
Computer Graphics
Geometry shaders can set the built-in variable gl_PrimitiveID to send a primitive number to the fragment shader
mjb – January 2, 2021
47
What Happens if you Exceed the Maximum Allowed Emitted Vertices?
New in GLSL 4.x – you can loop
back through the Geometry Shader
multiple times
Computer Graphics
mjb – January 2, 2021
48