(Demo) Wireframe Shader (OpenGL 3.2 and OpenGL ES 3.1)


GeeXLab - 3D demo - Wireframe shader OpenGL



I took a little moment few days ago to adapt a wireframe shader I found some years ago HERE. This wireframe shader is based on a vertex and a fragment shaders only, but requires the vertex shader to know the position of all vertices of a triangle. What’s more, this wireframe shader does not work with indexed meshes.

To make this wireframe shader compatible with indexed meshes that are very common in GeeXLab demos, I had to add a geometry stage in the GPU program. Thanks to the geometry shader that knows the position of all vertices of a triangle, it was easy to convert the original wireframe shader.

The demo is available in two versions, one for OpenGL 3.2 capable platforms (Windows, Linux and macOS), and the second one for ASUS Tinker Board that supports OpenGL ES 3.1. Among the new features introduced by OpenGL ES 3.1, we find the geometry shader support. Perfect!

You can download the GeeXLab demo from the following link:

Geekx3D download

The demo is also available in the full code sample pack in the gl-32/wireframe-shader/ folder).

You need a recent version of GeeXLab to run the demo. GeeXLab can be downloaded from the THIS PAGE.

Here are some screenshots of the demo on Windows. I added some controls to change the wireframe color as well as the one of the solid surface:


GeeXLab - 3D demo - Wireframe shader OpenGL

GeeXLab - 3D demo - Wireframe shader OpenGL

GeeXLab - 3D demo - Wireframe shader OpenGL

 
And here is the demo on the Tinker Board with the Mali T-760 GPU with OpenGL ES 3.1 support:


GeeXLab - 3D demo - Wireframe shader OpenGL ES 3.1 - ASUS Tinker Board

 
Here is the complete GPU program (OpenGL 3.2):

Vertex Shader

#version 150
in vec4 gxl3d_Position;
void main()
{
  gl_Position = gxl3d_Position;
}

Geometry Shader

#version 150
layout(triangles) in;
layout(triangle_strip, max_vertices=3) out;
uniform mat4 gxl3d_ModelViewProjectionMatrix; // GeeXLab auto uniform
uniform vec2 WIN_SCALE;

out vec3 dist;

void main()
{
  vec4 p0_3d = gl_in[0].gl_Position;
  vec4 p1_3d = gl_in[1].gl_Position;
  vec4 p2_3d = gl_in[2].gl_Position;

  // Compute the vertex position in the usual fashion. 
  p0_3d = gxl3d_ModelViewProjectionMatrix * p0_3d;  
  // 2D position
  vec2 p0 = p0_3d.xy / p0_3d.w; 

  // Compute the vertex position in the usual fashion. 
  p1_3d = gxl3d_ModelViewProjectionMatrix * p1_3d;  
  // 2D position
  vec2 p1 = p1_3d.xy / p1_3d.w; 

  // Compute the vertex position in the usual fashion. 
  p2_3d = gxl3d_ModelViewProjectionMatrix * p2_3d;  
  // 2D position
  vec2 p2 = p2_3d.xy / p2_3d.w; 
  
  
  
  //--------------------------------
  // Project p1 and p2 and compute the vectors v1 = p1-p0
  // and v2 = p2-p0                                  
  vec2 v10 = WIN_SCALE*(p1 - p0);   
  vec2 v20 = WIN_SCALE*(p2 - p0);   
  
  // Compute 2D area of triangle.
  float area0 = abs(v10.x*v20.y - v10.y*v20.x);
  
  // Compute distance from vertex to line in 2D coords
  float h0 = area0/length(v10-v20); 
  
  dist = vec3(h0, 0.0, 0.0);
  
  // Quick fix to defy perspective correction
  dist *= p0_3d.w;
  
  gl_Position = p0_3d;
  EmitVertex();
  


  //--------------------------------
  // Project p0 and p2 and compute the vectors v01 = p0-p1
  // and v21 = p2-p1                                  
  vec2 v01 = WIN_SCALE*(p0 - p1);   
  vec2 v21 = WIN_SCALE*(p2 - p1);   
  
  // Compute 2D area of triangle.
  float area1 = abs(v01.x*v21.y - v01.y*v21.x);
  
  // Compute distance from vertex to line in 2D coords
  float h1 = area1/length(v01-v21); 
  
  
  dist = vec3(0.0, h1, 0.0);
  
  // Quick fix to defy perspective correction
  dist *= p1_3d.w;
  
  gl_Position = p1_3d;
  EmitVertex();
  


  //--------------------------------
  // Project p0 and p1 and compute the vectors v02 = p0-p2
  // and v12 = p1-p2                                  
  vec2 v02 = WIN_SCALE*(p0 - p2);   
  vec2 v12 = WIN_SCALE*(p1 - p2);   
  
  // Compute 2D area of triangle.
  float area2 = abs(v02.x*v12.y - v02.y*v12.x);
  
  // Compute distance from vertex to line in 2D coords
  float h2 = area2/length(v02-v12); 
  
  dist = vec3(0.0, 0.0, h2);

  // Quick fix to defy perspective correction
  dist *= p2_3d.w;
  
  gl_Position = p2_3d;
  EmitVertex();

  //--------------------------------
  EndPrimitive();
  
}

Pixel Shader

#version 150
uniform vec3 WIRE_COL;
uniform vec3 FILL_COL;
in vec3 dist;
out vec4 FragColor;
void main()
{
  // Undo perspective correction.      
  //vec3 dist_vec = dist * gl_FragCoord.w;
  
  // Wireframe rendering is better like this:
  vec3 dist_vec = dist;
  
  // Compute the shortest distance to the edge
  float d = min(dist_vec[0], min(dist_vec[1], dist_vec[2]));

  // Compute line intensity and then fragment color
  float I = exp2(-2.0*d*d);

  FragColor.rgb = I*WIRE_COL + (1.0 - I)*FILL_COL; 
  FragColor.a = 1.0;
}




Leave a Comment

Your email address will not be published. Required fields are marked *