How to render full outlines as a post process - tutorial

I was looking for an outline implementation that could show all edges (like the middle image), not just the outer boundary (left), like the current outlines post processing example. I couldn’t find one, so I wrote one up and thought I’d share it here.

Hope this is helpful for anyone searching for this particular kind of effect! Happy to hear any feedback about implementation too.

My main “todo” here is to explore switching to a WebGLMultisampleRenderTarget to remove the need for the FXAA pass.

14 Likes

This is great! Thanks a lot for sharing and the tutorial. Presumably, there’s no way to control individual mesh edges. For example, changing the colour of a single meshes edges, or changing the opacity of a single meshes edges?

You could customize the effect for individual meshes by doing something similar to the OutlinePass effect here: three.js/OutlinePass.js at 5ad3317bf251bf0520d73704f2af66fafb770a77 · mrdoob/three.js · GitHub.

This has a selectedObjects array, and it basically hides all meshes but the ones in the array to only show outlines on those meshes.

You could do a similar thing to change the color of only one - so you’d have an outline pass for all but one meshes, and one just for the one mesh

1 Like

@user123 pointed out an issue using this with a few models.

In certain camera angles there are missing outlines, see the top left teeth of the gear here:

image

The reason these are missing is because at this angle, the normal buffer sees the gear and the surface behind it pointing in the same direction, so there’s no “edge” there:

image

However, this should be discernible using the depth buffer:

image

The problem is (1) the depth multiplier values in my live demo don’t go up high enough. And (2) the model is pretty big, so you’ll get a better result scaling the near/far planes (or scaling down the model).

Here’s a codesandbox with updated values to get the missing outlines: cylinder outlines test - CodeSandbox

Although it still misses the subtle edge in the circle on the inside of the gear. I wonder if there will always be artifacts like that in a screen-space approach like this, and whether there may be a more geometry-based approach that can produce correct results in all cases.

3 Likes

This is fantastic.
I’ve been looking for something like this forever!

EDIT: is there a way to show a lower res render on a higher res object?
Ie: you’ve got a smooth torus, (high res), yet only want to show the quad lines for every 4 quads or so in either dimension?

There’s a couple ways you could do this. There’s no way in the shader to do something like “every 4 quads” since it’s a post process effect. What you could do is tweak the normal multiplier and normal bias parameters.

Basically you want a lower normal multiplier, so that only sharper changes in surface have an outline, but smoother changes are not visible. But this will not help in the case of the torus where each quad differs only slightly so there are no sharp edges.

The other way is to:

  1. Load in a low poly version of each mesh
  2. Hide the low poly version from the main render scene
  3. Render the low poly instead of the main mesh in the outline pass

I haven’t looked into the approach in this thread: LDraw-like edges but if that works by creating the edges on startup on the CPU, that might give you more control (so you could say skip 3 quads etc).

Thanks! Really glad to hear you found this useful. I’d love to hear what kind of project you’re using this for if you don’t mind sharing.

1 Like

cool.
thanks for the tip!
do you think what you’re saying could be made any easier / more streamlined with
https://threejs.org/docs/#api/en/objects/LOD?

Not really. THREE.LOD looks like it will automatically change what version of the mesh is displayed based on distance to the camera. You still have to load in the high res/low res versions of the mesh yourself.

Great work, thanks for the update, I will have a play

1 Like

Great!! :clap: :clap: :clap:
I will use it
Thanks

1 Like