To create a shader program that utilizes the geometry shader to render silhouette edges, use the following steps:
- Use the following code for the vertex shader:
layout (location = 0 ) in vec3 VertexPosition; layout (location = 1 ) in vec3 VertexNormal; out vec3 VNormal; out vec3 VPosition; uniform mat4 ModelViewMatrix; uniform mat3 NormalMatrix; uniform mat4 ProjectionMatrix; uniform mat4 MVP; void main() { VNormal = normalize( NormalMatrix * VertexNormal); VPosition = vec3(ModelViewMatrix * vec4(VertexPosition,1.0)); gl_Position = MVP * vec4(VertexPosition,1.0); }
- Use the following code for the geometry shader:
layout( triangles_adjacency ) in; layout( triangle_strip, max_vertices = 15 ) out; out vec3 GNormal; out vec3 GPosition; // Which output primitives are silhouette edges flat out bool GIsEdge; in vec3 VNormal[]; // Normal in camera coords. in vec3 VPosition[]; // Position in camera coords. uniform float EdgeWidth; // Width of sil. edge in clip cds. uniform float PctExtend; // Percentage to extend quad bool isFrontFacing( vec3 a, vec3 b, vec3 c ) { return ((a.x * b.y - b.x * a.y) + (b.x * c.y - c.x * b.y) + (c.x * a.y - a.x * c.y)) > 0; } void emitEdgeQuad( vec3 e0, vec3 e1 ) { vec2 ext = PctExtend * (e1.xy - e0.xy); vec2 v = normalize(e1.xy - e0.xy); vec2 n = vec2(-v.y, v.x) * EdgeWidth; // Emit the quad GIsEdge = true; // This is part of the sil. edge gl_Position = vec4( e0.xy - ext, e0.z, 1.0 ); EmitVertex(); gl_Position = vec4( e0.xy - n - ext, e0.z, 1.0 ); EmitVertex(); gl_Position = vec4( e1.xy + ext, e1.z, 1.0 ); EmitVertex(); gl_Position = vec4( e1.xy - n + ext, e1.z, 1.0 ); EmitVertex(); EndPrimitive(); } void main() { vec3 p0 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w; vec3 p1 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w; vec3 p2 = gl_in[2].gl_Position.xyz / gl_in[2].gl_Position.w; vec3 p3 = gl_in[3].gl_Position.xyz / gl_in[3].gl_Position.w; vec3 p4 = gl_in[4].gl_Position.xyz / gl_in[4].gl_Position.w; vec3 p5 = gl_in[5].gl_Position.xyz / gl_in[5].gl_Position.w; if( isFrontFacing(p0, p2, p4) ) { if( ! isFrontFacing(p0,p1,p2) ) emitEdgeQuad(p0,p2); if( ! isFrontFacing(p2,p3,p4) ) emitEdgeQuad(p2,p4); if( ! isFrontFacing(p4,p5,p0) ) emitEdgeQuad(p4,p0); } // Output the original triangle GIsEdge = false; // Triangle is not part of an edge. GNormal = VNormal[0]; GPosition = VPosition[0]; gl_Position = gl_in[0].gl_Position; EmitVertex(); GNormal = VNormal[2]; GPosition = VPosition[2]; gl_Position = gl_in[2].gl_Position; EmitVertex(); GNormal = VNormal[4]; GPosition = VPosition[4]; gl_Position = gl_in[4].gl_Position; EmitVertex(); EndPrimitive(); }
- Use the following code for the fragment shader:
//*** Light and material uniforms... **** uniform vec4 LineColor; // The sil. edge color in vec3 GPosition; // Position in camera coords in vec3 GNormal; // Normal in camera coords. flat in bool GIsEdge; // Whether or not we're drawing an edge layout( location = 0 ) out vec4 FragColor; vec3 toonShade( ) { // *** toon shading algorithm from Chapter 4 *** } void main() { // If we're drawing an edge, use constant color, // otherwise, shade the poly. if( GIsEdge ) { FragColor = LineColor; } else { FragColor = vec4( toonShade(), 1.0 ); } }