| #version 400 core |
| |
| layout( triangles ) in; |
| layout( triangle_strip, max_vertices = 3 ) out; |
| |
| in EyeSpaceVertex { |
| vec3 position; |
| vec3 normal; |
| } gs_in[]; |
| |
| out WireframeVertex { |
| vec3 position; |
| vec3 normal; |
| noperspective vec4 edgeA; |
| noperspective vec4 edgeB; |
| flat int configuration; |
| } gs_out; |
| |
| uniform mat4 viewportMatrix; |
| |
| const int infoA[] = int[]( 0, 0, 0, 0, 1, 1, 2 ); |
| const int infoB[] = int[]( 1, 1, 2, 0, 2, 1, 2 ); |
| const int infoAd[] = int[]( 2, 2, 1, 1, 0, 0, 0 ); |
| const int infoBd[] = int[]( 2, 2, 1, 2, 0, 2, 1 ); |
| |
| vec2 transformToViewport( const in vec4 p ) |
| { |
| return vec2( viewportMatrix * ( p / p.w ) ); |
| } |
| |
| void main() |
| { |
| gs_out.configuration = int(gl_in[0].gl_Position.z < 0) * int(4) |
| + int(gl_in[1].gl_Position.z < 0) * int(2) |
| + int(gl_in[2].gl_Position.z < 0); |
| |
| // If all vertices are behind us, cull the primitive |
| if (gs_out.configuration == 7) |
| return; |
| |
| // Transform each vertex into viewport space |
| vec2 p[3]; |
| p[0] = transformToViewport( gl_in[0].gl_Position ); |
| p[1] = transformToViewport( gl_in[1].gl_Position ); |
| p[2] = transformToViewport( gl_in[2].gl_Position ); |
| |
| if (gs_out.configuration == 0) |
| { |
| // Common configuration where all vertices are within the viewport |
| gs_out.edgeA = vec4(0.0); |
| gs_out.edgeB = vec4(0.0); |
| |
| // Calculate lengths of 3 edges of triangle |
| float a = length( p[1] - p[2] ); |
| float b = length( p[2] - p[0] ); |
| float c = length( p[1] - p[0] ); |
| |
| // Calculate internal angles using the cosine rule |
| float alpha = acos( ( b * b + c * c - a * a ) / ( 2.0 * b * c ) ); |
| float beta = acos( ( a * a + c * c - b * b ) / ( 2.0 * a * c ) ); |
| |
| // Calculate the perpendicular distance of each vertex from the opposing edge |
| float ha = abs( c * sin( beta ) ); |
| float hb = abs( c * sin( alpha ) ); |
| float hc = abs( b * sin( alpha ) ); |
| |
| // Now add this perpendicular distance as a per-vertex property in addition to |
| // the position and normal calculated in the vertex shader. |
| |
| // Vertex 0 (a) |
| gs_out.edgeA = vec4( ha, 0.0, 0.0, 0.0 ); |
| gs_out.normal = gs_in[0].normal; |
| gs_out.position = gs_in[0].position; |
| gl_Position = gl_in[0].gl_Position; |
| EmitVertex(); |
| |
| // Vertex 1 (b) |
| gs_out.edgeA = vec4( 0.0, hb, 0.0, 0.0 ); |
| gs_out.normal = gs_in[1].normal; |
| gs_out.position = gs_in[1].position; |
| gl_Position = gl_in[1].gl_Position; |
| EmitVertex(); |
| |
| // Vertex 2 (c) |
| gs_out.edgeA = vec4( 0.0, 0.0, hc, 0.0 ); |
| gs_out.normal = gs_in[2].normal; |
| gs_out.position = gs_in[2].position; |
| gl_Position = gl_in[2].gl_Position; |
| EmitVertex(); |
| |
| // Finish the primitive off |
| EndPrimitive(); |
| } |
| else |
| { |
| // Viewport projection breaks down for one or two vertices. |
| // Caclulate what we can here and defer rest to fragment shader. |
| // Since this is coherent for the entire primitive the conditional |
| // in the fragment shader is still cheap as all concurrent |
| // fragment shader invocations will take the same code path. |
| |
| // Copy across the viewport-space points for the (up to) two vertices |
| // in the viewport |
| gs_out.edgeA.xy = p[infoA[gs_out.configuration]]; |
| gs_out.edgeB.xy = p[infoB[gs_out.configuration]]; |
| |
| // Copy across the viewport-space edge vectors for the (up to) two vertices |
| // in the viewport |
| gs_out.edgeA.zw = normalize( gs_out.edgeA.xy - p[ infoAd[gs_out.configuration] ] ); |
| gs_out.edgeB.zw = normalize( gs_out.edgeB.xy - p[ infoBd[gs_out.configuration] ] ); |
| |
| // Pass through the other vertex attributes |
| gs_out.normal = gs_in[0].normal; |
| gs_out.position = gs_in[0].position; |
| gl_Position = gl_in[0].gl_Position; |
| EmitVertex(); |
| |
| gs_out.normal = gs_in[1].normal; |
| gs_out.position = gs_in[1].position; |
| gl_Position = gl_in[1].gl_Position; |
| EmitVertex(); |
| |
| gs_out.normal = gs_in[2].normal; |
| gs_out.position = gs_in[2].position; |
| gl_Position = gl_in[2].gl_Position; |
| EmitVertex(); |
| |
| // Finish the primitive off |
| EndPrimitive(); |
| } |
| } |