Easiest way is to still use glPolygonMOde(...)
while rendering QUADS instead of TRIANGLES. This however requires change in your data ...
In case your meshes will be always composed from QUADS and triangulated so the 2 consequent triangles always form a QUAD but you are unwilling to switch to QUADS then you can write a Geometry shader taking in 6 Vertexes and outputting 4 lines which will be still more or less "fast" however note that Geometry shaders where not very reliable in past...
If those are not an option you can modify this:
To render fragments near sharp normal changes. However this will hide back sides so in order to have them too you have to do this in 2 passes one with glFrontFace(GL_CW);
and the other with glFrontFace(GL_CCW);
combining their outputs. This also allows you to set different color to the hidden edges ...
So I see it like this:
glFrontFace(GL_CW);
render_mesh_to_trexture(); // using normal instead of color
render_siluete_from_texture(); // using texture from previous line
glFrontFace(GL_CCW);
render_mesh_to_trexture(); // using normal instead of color
render_siluete_from_texture(); // using texture from previous line
However this will be a lot slower and requires consistent winding rule...
Another option is to just compile list of edges from your mesh on CPU side and remove all that have duplicates. Its simple and fast however will create new VBO/VAO data and no longer complies with your requirement to use the same data (however the new VBO might be just integer indices so 2 ints per edge).