3

What is the best method to get the outline effect with animated skinned mesh?

ThreeJS Outline issue

An example with the model paused at a specific pose: https://jsfiddle.net/Eketol/uev9o0qp/

composer = new THREE.EffectComposer( renderer );
renderPass = new THREE.RenderPass(scene, camera);
renderPass.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderPass);

outlinePass = new THREE.OutlinePass( new THREE.Vector2( this.viewWidth, this.viewHeight ), scene, camera );
outlinePass.visibleEdgeColor.set( 0xff0000 );
outlinePass.hiddenEdgeColor.set( 0xffff00 );
outlinePass.edgeGlow = 0;
outlinePass.edgeThickness = 0.3; // Default is 1.
outlinePass.edgeStrength = 3; // Default is 3.
outlinePass.setSize(window.innerWidth, window.innerHeight);
composer.addPass(outlinePass);

Another example with animated model: https://jsfiddle.net/Eketol/4xcd365w/

As far as I understand, this is due to the transformations being done in the graphics card, so the code doesn't have a reference of the vertex positions. This issue also affects to the raycaster.

I have read multiple attempts and experiments. Some of them use a glow material, others use two or even three mesh instances with multiple render passes to get the outline done (eg: http://jsfiddle.net/Nv7Up/).

Cons using glow material:

  • The outline thickness will change when the camera distance changes.
  • The outline may not be fully visible if there is another object in front of the selected one.
  • Objects with multiple meshes may look a bit strange (although I could live with it).

Cons using multiple meshes:

  • Maybe ok for simple objects with low polygons, but a hell for custom objects with multiple meshes? Eg: an army of soldiers with random accessories like swords, shields, etc. Not sure how complex would be or what performance cost it would have to clone every skinned object with their custom properties.
  • Instead of using multiple meshes, wouldn't be easier using the original mesh and render it in a secondary scene to get the mask or outline?

Questions:

  1. Is it possible to get the current ThreeJS Outline effect working properly with animated SkinnedMesh?
  2. If the transformed vertex info is on the graphics card, is it possible for the outline shader to get either the vertex info or the rendered image directly from it?
  3. If the above is not possible, is there a method to apply the SkinnedMesh transformations to the vertices so the shader has the correct pose info?
Eketol
  • 125
  • 10
  • What you understand is not how it actually works. `OutlinePass.prepareMaskMaterial` is used to render `OutlinePass.selectedObjects`, and this material is very basic therefore it does not support skeletal animations. It is certainly possible to get this effect working with skeletal animations and whatever you want, though there might be no implementations, and I doubt someone here will implement it for you. – StrandedKitty Jan 19 '20 at 12:27

2 Answers2

2

Is it possible to get the current ThreeJS Outline effect working properly with animated SkinnedMesh?

Yes, however an enhancement of an internal vertex shader is necessary. I've added the respective shader chunks in this updated fiddle (morphtarget_pars_vertex, skinbase_vertex, skinning_pars_vertex, begin_vertex, morphtarget_vertex, skinning_vertex, project_vertex).

https://jsfiddle.net/35vrtm42/

But notice that the horse animation is based on morph targets.

With this enhancement, you only have to tell the OutlinePass to enable the respective animation type. For the fiddle, it was necessary to do this.

outlinePass.depthMaterial.morphTargets = true;
outlinePass.prepareMaskMaterial.morphTargets = true;

If your model uses skeletal animation, just replace the morphTargets property with skinning.

three.js R112

Mugen87
  • 28,829
  • 4
  • 27
  • 50
  • Sorry Mugen87, I made the 'skinning' replacements, but I get the following error with the stormtrooper example: `THREE.WebGLProgram: shader error: 1286 35715 false gl.getProgramInfoLog No compiled vertex shader when at least one graphics shader is attached.` See: https://jsfiddle.net/Eketol/5z3u047m/ – Eketol Jan 20 '20 at 13:32
  • Sorry, a shader chunk was still missing. Let me update the answer: https://jsfiddle.net/6k1x8bfq/ – Mugen87 Jan 20 '20 at 13:39
  • Perfect now. Thanks again! – Eketol Jan 20 '20 at 14:16
  • BTW: With the next release `R113`, the shader modifications are already done. So you can use the file directly from the repo/npm package. – Mugen87 Jan 21 '20 at 16:24
0

in OutlinePass.js v121 (6-Okt-2020) L38, the skinning parameter is missing:

    this.depthMaterial = new THREE.MeshDepthMaterial();
    this.depthMaterial.side = THREE.DoubleSide;
    this.depthMaterial.depthPacking = THREE.RGBADepthPacking;
    this.depthMaterial.blending = THREE.NoBlending;

    this.prepareMaskMaterial = this.getPrepareMaskMaterial();
    this.prepareMaskMaterial.side = THREE.DoubleSide;
    this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ( this.prepareMaskMaterial.fragmentShader, this.renderCamera );

whereas it should be like this

 this.depthMaterial = new THREE.MeshDepthMaterial();
    this.depthMaterial.side = THREE.DoubleSide;
    this.depthMaterial.depthPacking = THREE.RGBADepthPacking;
    this.depthMaterial.blending = THREE.NoBlending;
    this.depthMaterial.skinning = true; // <-------

    this.prepareMaskMaterial = this.getPrepareMaskMaterial();
    this.prepareMaskMaterial.side = THREE.DoubleSide;
    this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ( this.prepareMaskMaterial.fragmentShader, this.renderCamera );
    this.prepareMaskMaterial.skinning = true;   // <-----
Dimitrios Ververidis
  • 1,118
  • 1
  • 9
  • 33