[vertex shader] /////////////////// /** Definitions **/ #version 420 core // OpenGL 4.2 / GLSL 420 ///////////// /** Input **/ layout(location = 0) in vec4 vertex; // Vertex positions at attribute 0 //////////////// /** Uniforms **/ uniform mat4 ModelMatrix; // Model transformation matrix ////////////// /** Output **/ out vec3 worldPos; // World-space vertex position ////////////////// /** Procedures **/ /* Main */ void main() { // Transform vertices by their model matrix from local-space position to world-space position worldPos = (ModelMatrix * vertex).xyz; } [geometry shader] /////////////////// /** Definitions **/ #version 420 core // OpenGL 4.2 / GLSL 420 layout(triangles_adjacency) in; // Input triangle with adjacencies layout(triangle_strip, max_vertices = 18) out; // Output triangle strips, 18 vertices at most ///////////// /** Input **/ in vec3 worldPos[]; // World-space positions //////////////// /** Uniforms **/ uniform mat4 ViewProjectionMatrix; // View * Projection matrix uniform vec3 LightPos; // Light position (in world-space) uniform vec3 Offset; // Offset to single object (to avoid z-fighting), vector from light to object center can work just fine ////////////////// /** Procedures **/ /* Emit Triangle */ // Creates triangle from specified 3 vertices (v0, v1 and v2) void emit_triangle(in vec4 v0, in vec4 v1, in vec4 v2) { gl_Position = ViewProjectionMatrix * v0; EmitVertex(); gl_Position = ViewProjectionMatrix * v1; EmitVertex(); gl_Position = ViewProjectionMatrix * v2; EmitVertex(); EndPrimitive(); } /* Process Edge */ // Determines whether edge(e0, e1) is part of silhouette (e.g. adjacent triangle (e1, adj, e0) isn't lit by light), if so, then extrudes this edge to infinity void process_edge(in vec3 e0, in vec3 e1, in vec3 adj) { vec3 n = cross(e1 - adj, e0 - adj); vec3 l = LightPos - adj; if(dot(n, l) < 0.0) { gl_Position = ViewProjectionMatrix * vec4(e0 + Offset, 1.0); EmitVertex(); gl_Position = ViewProjectionMatrix * vec4(e0 - LightPos, 0.0); EmitVertex(); gl_Position = ViewProjectionMatrix * vec4(e1 + Offset, 1.0); EmitVertex(); gl_Position = ViewProjectionMatrix * vec4(e1 - LightPos, 0.0); EmitVertex(); EndPrimitive(); } } /* Main */ void main() { // Determine whether current triangle is lit by light vec3 normal = cross(worldPos[2] - worldPos[0], worldPos[4] - worldPos[0]); vec3 lightDir = normalize(LightPos - worldPos[0]); if(dot(normal, lightDir) >= 0.0) { // If so, emit front cap emit_triangle(vec4(worldPos[0] + Offset, 1), vec4(worldPos[2] + Offset, 1), vec4(worldPos[4] + Offset, 1)); // Emit back cap emit_triangle(vec4(worldPos[0] - LightPos, 0), vec4(worldPos[4] - LightPos, 0), vec4(worldPos[2] - LightPos, 0)); // And process whether edges are part of silhouette (if so, extrude them) process_edge(worldPos[0], worldPos[2], worldPos[1]); process_edge(worldPos[2], worldPos[4], worldPos[3]); process_edge(worldPos[4], worldPos[0], worldPos[5]); } } [pixel shader] /////////////////// /** Definitions **/ #version 420 core // OpenGL 4.2 / GLSL 420 ////////////// /** Output **/ out vec4 mFragColor; // Fragment color output ////////////////// /** Procedures **/ /* Main */ void main() { // Output just black color (basically we dont write to color buffer so we don't care about color) mFragColor = vec4(0.0, 0.0, 0.0, 0.0); }