Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Render a mesh as wireframe

edited July 2013 in Shaders Posts: 22

This is a way to render meshes as wireframe without drawing the lines yourself, using a shader.

I started with this: http://i.imgur.com/41dbPXc.jpg

Since the shader can't just go and draw random lines all over the place, we have to do it the other way around and render fragments according to their distance to the nearest edge. One way to do that is setting the vertex colors of each triangle to red, green, and blue. This will get interpolated for the fragment shader, and at each edge, one of the three color components will be close to zero.

It looks something like this: http://i.imgur.com/uc37VYG.jpg

As you can see the edge from a red corner to a green corner has no blue, and so on.

You can set these vertex colors on an existing mesh like this:

function set_wireframe_colors(m)
    local cc = {}
    for i = 1, m.size/3 do
        table.insert(cc, color(255,0,0))
        table.insert(cc, color(0,255,0))
        table.insert(cc, color(0,0,255))
    end
    m.colors = cc
end

Now all that's needed is a shader that knows how to interpret this color data.

The vertex shader is simple, we just copy the vertex color to a varying for the fragment shader:

attribute vec4 position;
attribute vec3 color;
uniform mat4 modelViewProjection;
varying highp vec3 triangle;
void main(void) {
    triangle = color;
    gl_Position = modelViewProjection * position;
}

The fragment shader is also not too complex, but I think the standard derivatives functions are not available on older devices:

#extension GL_OES_standard_derivatives : enable
uniform highp vec4 wire_color;
uniform highp vec4 fill_color;
uniform highp float wire_width;
varying highp vec3 triangle;
void main(void) {
    highp vec3 d = fwidth(triangle);
    highp vec3 tdist = smoothstep(vec3(0.0), d*wire_width, triangle);
    gl_FragColor = mix(wire_color, fill_color, min(min(tdist.x, tdist.y), tdist.z));
}

The result looks like this: http://i.imgur.com/puUuq3l.jpg

Well, it actually looks better than that because jpeg compression is not good to thin lines :D

The fragment shader supports two attributes, wire_color and fill_color, to set colors (may have alpha), and wire_width to set line width. Culled faces are missing, but of course this is only an issue if you use transparency.

On older devices that don't support the SD extension, you can use this fragment shader instead, but it will render lines with varying width and it looks a little messy:

uniform highp vec4 wire_color;
uniform highp vec4 fill_color;
uniform highp float wire_width;
varying highp vec3 triangle;
void main(void) {
    if(min(min(triangle.x, triangle.y), triangle.z) < wire_width/10.0) {
        gl_FragColor = wire_color;
    } else {
        gl_FragColor = fill_color;
    }
}

Comments

  • edited July 2013 Posts: 391

    @damny, Amazing stuff here. This is something I have been looking for. I have been messing with 3D rendering myself but haven't got the meshes down yet. Currently I have been drawing all the lines using a recursive function so I could see what I was building, but this makes the project lag very hard when I try and render more complex shapes. This shader technique is so much simpler and so much cleaner. I love it!! Thanks for sharing.

  • Posts: 455

    Very nice. A long time ago I used a cheat of just sticking a wireframe like texture on the object, this is an algorithm I had thought about but never got around to.

    The slightly odd thing, is that traditionally wireframe was a mode you used for lighter performance, but in Codea we want it for the visual look, and it actually makes you work harder ;-)

  • edited July 2014 Posts: 1

    Very cool, thanks for sharing!

  • IgnatzIgnatz Mod
    Posts: 5,396

    @greg7mdp - you probably didn't notice, but this thread is a year old. We prefer not to reopen really old threads if possible. I'll close it now. ;)

This discussion has been closed.