1

I'm digging into advanced features for my app with ThreeJS. I'm developing an outline feature, allowing to outline a mesh in a scene composed of multiple meshes.

My first implementation was made by duplicating the geometry, scaling it a little bit and then applying a specific shader for the outline. The main problem is that if I have to highlight my whole scene, I will need twice the memory to duplicate each objects.

That's why a decided to use the overrideMaterial attribute of THREE.Scene, to be able to make a second rendering pass without duplicating the geometry. In order to have the outline effect, I just used the stencil buffer between those two passes :

function render() {
    gl.enable(gl.STENCIL_TEST);
    gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);

    renderer.clear();

    gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
    gl.stencilMask(0xFF);
    renderer.render(scene, camera);

    gl.stencilFunc(gl.NOTEQUAL, 1, 0xFF);
    gl.stencilMask(0x00);

    scene.overrideMaterial = outlineMaterial;
    renderer.render(scene, camera);
    scene.overrideMaterial = null;
    gl.stencilMask(0xFF);
    gl.disable(gl.STENCIL_TEST);
}

enter image description here

The problem is that because the stencil buffer is filled between two render passes (and not two objects render call), my highlight is not fully done when an object is placed behind the object I want to highlight.

Does someone got an idea to solve this without having to modify ThreeJS code? The example is here : http://jsfiddle.net/Ludobaka/z2fLurLr/

Edit :

I'm creating a molecule visualisation tool. A molecule is composed of residues which are composed of atoms. For performance enhancing purpose, I'm only creating the big mesh corresponding to the molecule (otherwise for each molecule, I will have nbAtoms Object3D, which is a lot). I want to be able to highlight sub-parts of this mesh corresponding to a group of residues or group of atoms or even the whole molecule itself. That's why I decided to perform two render passes instead of just duplicating the geometry as it's done in the example below.

By duplicating the geometry, I will have to extract those subparts, create a new mesh for outline and manage them to avoid keeping them when they're not used.

  • take a look at https://stemkoski.github.io/Three.js/Outline.html – gaitat Mar 03 '16 at 16:22
  • Thanks for the example. I would prefer if I don't need to duplicate the mesh into my scene. That's why I thought using the overrideMaterial and do 2 rendering pass would be better. Maybe I'm going on the wrong way. – Ludovic Bailly Mar 03 '16 at 16:34
  • so if your scene has thousands of objects you prefer to re-render it instead of instancing the geometry? – gaitat Mar 03 '16 at 19:53
  • 1
    See http://stackoverflow.com/questions/23183507/outline-object-normal-scale-stencil-mask-three-js Its coffeescript though. – Fabian Schlieper May 11 '16 at 12:51
  • Scaling the mesh only works with certain mesh geometries. For a generic solution, the mesh must be dilated, not simply scaled. That coffeescript solution does not work for general meshes even though it attempts to offset the vertices. – ricosrealm Oct 03 '20 at 06:59

1 Answers1

1

You'll need to create a custom ShaderMaterial to do it. Render your outlined object(s) to a texture that is (ideally) the size of your destination framebuffer, then render a framebuffer-sized quad using that texture and have the fragment shader blur or do other image transforms. I have an example here that uses raw WebGL, you can adapt the shaders for three.js.

prewett
  • 1,587
  • 14
  • 19