Down to the Wire

One of the new types of materials supported in v3.0 is called Wire. This material basically renders only mesh edges, leaving the interior of each face transparent. Sounds simple, eh? Well, as it turns out, it’s not.

This little project started out with me wanting to render wireframes (and support doing so in the framework) for debugging purposes. It rapidly evolved into a brand-new material that, as you will see, can serve a variety of purposes even in production applications!

There are two common ways to render wireframes in WebGL, and both of them are kind of a cheat. First, you can just change the draw mode from GL_TRIANGLES to GL_LINE_STRIP; this gives a close approximation of what the wireframe should be, but doesn’t look that great because triangles (and even triangle strips) just don’t translate that cleanly into lines. You end up with extra lines or missing lines all over the place. The other method is to actually rebuild the mesh to use bona-fide lines (or line strips) instead of triangles. Both of these are too heavy-handed, in my opinion, and the results are usually not that great — either it runs or initializes too slowly, or it doesn’t look very good, or both. Plus, it usually requires some added up-front work by the developer, which in Jax is against the rules.

In standard desktop OpenGL, rendering a wireframe is a simple process as far as the developer is concerned. One line of code changes the polygon mode from GL_FRONT_AND_BACK (or whatever) to GL_LINE, and you’re done. Unfortunately, this doesn’t translate so well to WebGL, where you can’t control the polygon mode.

This led me into the world of shader-based wireframes. The problem with doing wireframes in the shader is that you only ever have information about a single vertex at a time — and with information about only a single vertex, you can’t hope to draw a line between two or more of them.

Apparently, one of the most common techniques for drawing “correct” wireframes has been to draw objects in two passes, using polygon offsetting to shift one version forward just a tiny bit. This is not only slow (obviously, twice the renders takes twice the time), but it can also produce visual artifacts that don’t quite look right. So instead, I implemented a single-pass algorithm as discussed in this paper.

As you can see, the result is quite easy on the eyes — much more so than the old OpenGL polygon mode would have been, I have to admit!

What’s nice about Wire is that it is a material, and as such gains all of the benefits and perks of being one. The Wire layer can be used on its own, or merged into a series of other layers to produce a very nice combination of them all. As a result, the Wire material can be textured; it can receive ambient, diffuse and specular shading from local light sources; it can cast and receive shadows; and it can do basically anything you’d expect any other material to be able to do.

In short, Jax.Material.Wire is now a first-class material that can be used for much more than just debugging!

Next Steps

This is as far as I’m going to take the Wire material for Jax v3. However, I’m not done with wires in general. In a future release of Jax, I’d like to see Wire become even more versatile, allowing you to style the lines themselves: for example, it’d be neat to support tapering lines so that they are thicker at intersections and thinner in the middle, or becoming thinner as they get farther away from the camera to give a better sense of depth. There’s a lot that can be done with Wire, and we’re just getting warmed up!

Comments are closed.