247

How do you render primitives as wireframes in OpenGL?

4
  • 2
    Be more specific on the OpenGL version you are using. Commented Mar 17, 2016 at 11:53
  • With ModernGL you can simpli use the wireframe attribute of the Context class Commented May 30, 2017 at 10:07
  • use GL_LINES to draw wireframes Commented Apr 17, 2020 at 13:27
  • This will help for most reader as it explains how to implement it learnopengl.com/Getting-started/Hello-Triangle Commented Feb 16, 2021 at 13:26

11 Answers 11

415
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

to switch on,

glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

to go back to normal.

Note that things like texture-mapping and lighting will still be applied to the wireframe lines if they're enabled, which can look weird.

Sign up to request clarification or add additional context in comments.

Comments

49

From http://cone3d.gamedev.net/cgi-bin/index.pl?page=tutorials/ogladv/tut5

// Turn on wireframe mode
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);

// Draw the box
DrawBox();

// Turn off wireframe mode
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);

2 Comments

making two calls is redundant. use GL_FRONT_AND_BACK
As an addendum to @shoosh's comment, the Red Book states that GL_FRONT and GL_BACK have been deprecated and removed from OpenGL 3.1 and up. Now, you can still use them through the compatibility extension, but if you have a choice between forward-compatible and backward-compatible, I would recommend going for the former.
30

Assuming a forward-compatible context in OpenGL 3 and up, you can either use glPolygonMode as mentioned before, but note that lines with thickness more than 1px are now deprecated. So while you can draw triangles as wire-frame, they need to be very thin. In OpenGL ES, you can use GL_LINES with the same limitation.

In OpenGL it is possible to use geometry shaders to take incoming triangles, disassemble them and send them for rasterization as quads (pairs of triangles really) emulating thick lines. Pretty simple, really, except that geometry shaders are notorious for poor performance scaling.

What you can do instead, and what will also work in OpenGL ES is to employ fragment shader. Think of applying a texture of wire-frame triangle to the triangle. Except that no texture is needed, it can be generated procedurally. But enough talk, let's code. Fragment shader:

in vec3 v_barycentric; // barycentric coordinate inside the triangle
uniform float f_thickness; // thickness of the rendered lines

void main()
{
    float f_closest_edge = min(v_barycentric.x,
        min(v_barycentric.y, v_barycentric.z)); // see to which edge this pixel is the closest
    float f_width = fwidth(f_closest_edge); // calculate derivative (divide f_thickness by this to have the line width constant in screen-space)
    float f_alpha = smoothstep(f_thickness, f_thickness + f_width, f_closest_edge); // calculate alpha
    gl_FragColor = vec4(vec3(.0), f_alpha);
}

And vertex shader:

in vec4 v_pos; // position of the vertices
in vec3 v_bc; // barycentric coordinate inside the triangle

out vec3 v_barycentric; // barycentric coordinate inside the triangle

uniform mat4 t_mvp; // modeview-projection matrix

void main()
{
    gl_Position = t_mvp * v_pos;
    v_barycentric = v_bc; // just pass it on
}

Here, the barycentric coordinates are simply (1, 0, 0), (0, 1, 0) and (0, 0, 1) for the three triangle vertices (the order does not really matter, which makes packing into triangle strips potentially easier).

The obvious disadvantage of this approach is that it will eat some texture coordinates and you need to modify your vertex array. Could be solved with a very simple geometry shader but I'd still suspect it will be slower than just feeding the GPU with more data.

16 Comments

I thought this looked promising, however this doesn't appear to work in any capacity with OpenGL 3.2 (non-ES). Although it's possible I messed something up, I played around with this for quite a bit and am not really sure how this is even intended to be used. Any more information on what you intend to actually render with these shaders would be helpful; I don't see how anything other than a fill would produce anything useful, but that doesn't even work given that the alpha value of gl_FragColor appeared to be entirely ignored in OpenGL 3.2...
Of course it does. You can refer to someone's implementation of the same idea in stackoverflow.com/questions/7361582/….
The problem I run into trying to use your suggested implementation is, I think, that the shader will never be drawing outside of the object to be outlined. This will therefore draw the outline over the object being outlined? In other words, the outline will be purely contained within the original object to be drawn?
@BrunoLevy you can get additional coordinated in webGL, no? If you are lucky, you might be able to get those coordinates from texcoords if you have very simple models but otherwise you just need to add new coordinates. Would be too nice if it worked without it :). All you need is to pass a single scalar per vertex (and then expand it to a 3-vector in the vertex shader).
Ah, now I see ... f_width() is my friend, in this respect. Thanks
|
10

In Modern OpenGL(OpenGL 3.2 and higher), you could use a Geometry Shader for this:

#version 330

layout (triangles) in;
layout (line_strip, max_vertices=3) out;

in vec2 texcoords_pass[]; // texcoords from Vertex Shader
in vec3 normals_pass[]; // normals from Vertex Shader

out vec3 normals; // normals for Fragment Shader
out vec2 texcoords; // texcoords for Fragment Shader

void main(void)
{
    int i;
    for (i = 0; i < gl_in.length(); i++)
    {
        texcoords=texcoords_pass[i]; // pass through
        normals=normals_pass[i]; // pass through
        gl_Position = gl_in[i].gl_Position; // pass through
        EmitVertex();
    }
    EndPrimitive();
}

Comments

5

If you are using the fixed pipeline (OpenGL < 3.3) or the compatibility profile you can use

//Turn on wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

//Draw the scene with polygons as lines (wireframe)
renderScene();

//Turn off wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

In this case you can change the line width by calling glLineWidth

Otherwise you need to change the polygon mode inside your draw method (glDrawElements, glDrawArrays, etc) and you may end up with some rough results because your vertex data is for triangles and you are outputting lines. For best results consider using a Geometry shader or creating new data for the wireframe.

Comments

2

The easiest way is to draw the primitives as GL_LINE_STRIP.

glBegin(GL_LINE_STRIP);
/* Draw vertices here */
glEnd();

2 Comments

not if it is a bunch of GL_TRIANGLES: you would get lines between them. In OpenGL 1.x or legacy you use glPolygonMode. In recent OpenGL you play with the geometry shader.
This is an ancient way of doing things that needs to be left behind.
2

Use this function:

void glPolygonMode(GLenum face, GLenum mode);

face: Specifies the polygon faces that mode applies to. Can be GL_FRONT for the front side of the polygon, GL_BACK for the back and GL_FRONT_AND_BACK for both.

mode: Three modes are defined.

  • GL_POINT: Polygon vertices that are marked as the start of a boundary edge are drawn as points.

  • GL_LINE: Boundary edges of the polygon are drawn as line segments. (your target)

  • GL_FILL: The interior of the polygon is filled.

P.S: glPolygonMode controls the interpretation of polygons for rasterization in the graphics pipeline.

For more information look at the OpenGL reference pages in khronos group.

Comments

1

You can use glut libraries like this:

  1. for a sphere:

    glutWireSphere(radius,20,20);
    
  2. for a Cylinder:

    GLUquadric *quadratic = gluNewQuadric();
    gluQuadricDrawStyle(quadratic,GLU_LINE);
    gluCylinder(quadratic,1,1,1,12,1);
    
  3. for a Cube:

    glutWireCube(1.5);
    

Comments

0

If it's OpenGL ES 2.0 you're dealing with, you can choose one of draw mode constants from

GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, to draw lines,

GL_POINTS (if you need to draw only vertices), or

GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_TRIANGLES to draw filled triangles

as first argument to your

glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices)

or

glDrawArrays(GLenum mode, GLint first, GLsizei count) calls.

Comments

0

A good and simple way of drawing anti-aliased lines on a non anti-aliased render target is to draw rectangles of 4 pixel width with an 1x4 texture, with alpha channel values of {0.,1.,1.,0.}, and use linear filtering with mip-mapping off. This will make the lines 2 pixels thick, but you can change the texture for different thicknesses. This is faster and easier than barymetric calculations.

Comments

0

You basically have an option for the mode of rendering in any OpenGL draw call, Like

glDraw....(GLenum mode, ...);

You can render primitives as wireframe by changing that mode. You have several options for render mode in OpenGL draw calls,

GL_LINES
GL_TRIANGLES
GL_POINTS
...

So basically you can pass GL_LINES as the mode in your draw call for rendering primitives as wireframe.

glDraw...(GL_LINES, ...);

1 Comment

It's important to note, you cant just pass in GL_LINES to the given draw call. You would have to change the indices to match for this solution to work. Additionally, if there is an advantage to doing it this way over using glPolygonMode(GL_FRONT, GL_LINE); glPolygonMode(GL_BACK, GL_LINE); It would be worth adding that to your answer