[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);
}