Dynamic face outlines based on occlusion

I would like to create a new line material that would be used to highlight objects’s edges, similar to what CAD programs do, when face outlines are shown. First I experimented with LineSegments2, LineSegmentsGeometry, and LineMaterial, but I think I will have to create something custom.

These are my requirements:

  1. I want to show the edges of an object’s faces.
  2. The color and thickness of the edge must be configurable.
  3. There shouldn’t be Z fighting between the drawn edge and the faces.
  4. I want to draw the edges that are occluded by the object itself, but with a different color (and possibly translucent).

Here is an example of what I want to achieve. Visible edges are black, occluded edges are white.

Items 1-3 are solvable with the aforementioned three classes. My main headache is the 4th item in the list. My current idea is to create a DepthTexture, pass it to the material and to the custom fragment shader and use the depth information to decide if the line is occluded or not. Maybe a separate render target and two render passes will be necessary for this, I not sure about that. To be honest, this whole thing feels a bit “hacky” and inefficient but I can’t think of anything better. Do you think this solution is feasible? Are there better solutions to this problem?

You can change the set of white lines to use depthFunc: THREE.GreaterDepth so they only render when they’re behind something that’s already been rendered:

// base mesh
const mesh = new THREE.Mesh( ... );

// Create the edge geometry
const lineGeometry = new THREE.EdgesGeometry( geometry );

// Create a line segments object for rendering obscured lines. This done by inverting
// the depth function and setting the render order to a higher number so it only renders
// after the base mesh has.
const backgroundLines = new THREE.LineSegments(
  lineGeometry,
  new THREE.LineBasicMaterial( {
    color: 0xffffff,
    depthFunc: THREE.GreaterDepth,
    depthWrite: false,
  } ) );
backgroundLines.renderOrder = 1;

// Render the foreground lines last so they're on top of the background lines
const foregroundLines = new THREE.LineSegments(
  lineGeometry,
  new THREE.LineBasicMaterial( {
    color: 0,
  } ) );
foregroundLines.renderOrder = 2;

group.add( foregroundLines, backgroundLines );

You can see an example here:

4 Likes

Thanks! This is the kind of solution where in hindsight it seems so trivial, but I don’t know if I would have ever thought about it. :slight_smile:

1 Like