Geometry Shaders
Geometry shaders run once per primitive and can emit zero or more primitives.
Wireframe overlay in the fragment shader:
Uses: visualizing normals (emit lines), point-sprite expansion, fur/grass shells, layered rendering (gl_Layer for cube-map shadows in one pass), shadow volumes, instanced geometry (layout(invocations = N)).
⚠️ Geometry shaders are often slow (especially with large amplification) — prefer instancing, compute shaders, or mesh shaders for heavy generation.
#version 330 core
layout (triangles) in; // points, lines, lines_adjacency, triangles, triangles_adjacency
layout (triangle_strip, max_vertices = 3) out; // points, line_strip, triangle_strip
in vec3 vNormal[]; // inputs are arrays (one per vertex)
out vec3 gNormal;
out vec3 gBarycentric;
uniform float uExplode;
uniform float uTime;
void main() {
vec3 a = gl_in[0].gl_Position.xyz, b = gl_in[1].gl_Position.xyz, c = gl_in[2].gl_Position.xyz;
vec3 faceN = normalize(cross(b - a, c - a));
const vec3 bary[3] = vec3[](vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1));
for (int i = 0; i < 3; ++i) {
gl_Position = gl_in[i].gl_Position + vec4(faceN * uExplode * (sin(uTime) + 1.0), 0.0); // "explode" effect
gNormal = vNormal[i];
gBarycentric = bary[i]; // for wireframe rendering in the fragment shader
EmitVertex();
}
EndPrimitive();
}float edge = min(min(gBarycentric.x, gBarycentric.y), gBarycentric.z);
float w = fwidth(edge);
float line = 1.0 - smoothstep(0.0, w * 1.5, edge);
outColor = mix(baseColor, vec4(1.0), line);