2

I'm trying to draw lines on a face in three.js

Everything's working fine except the lines are barely visible - no matter how thick I make them: they look like this:

barely visible lines

The code that draws the line is:

var lgeometry = new THREE.Geometry();
var lmaterial = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 4 } );

var v1 = p1;
var v2 = p2;

lgeometry.vertices.push(v1);
lgeometry.vertices.push(v2);

console.log(lgeometry);
var line = new THREE.Line( lgeometry, lmaterial );

scene.add( line );

I suspect that - since the lines are exactly on the surface, they don't get rendered (is this what the call z-fighting?)

Is there a way to solve this?

I am considering:

  • drawing cylinders or other shapes instead of lines
  • drawing the line barely above the surface along normals

Any advice or direction to move in?

gman
  • 100,619
  • 31
  • 269
  • 393
simone
  • 4,667
  • 4
  • 25
  • 47
  • Displace the vertices of the line according to the face normal might be the easiest way since you still can use lines. The amount of displacement will depend on your view frustum so you have to play around with different values. BTW: Setting `linewidth` with `LineBasicMaterial` has no effect. Line primitives are always rendered with a width of 1 (pixel). – Mugen87 Mar 20 '19 at 13:02
  • @simone See `polygonOffset` in https://stackoverflow.com/questions/31539130/display-wireframe-and-solid-color/31541369#31541369. The lines must be in the plane of the faces. That may not be true in your case. – WestLangley Mar 20 '19 at 16:28

1 Answers1

1

This is achievable with combination of layers and stencil buffers.

Working Demo: https://jsfiddle.net/mmalex/dg417kvn/

how to fix z-fighting issues with stencil buffers


Solution:

For explanation, please follow the comments in code below:

document.fixZFighting = function() {
    // 1. Get the current WebGL context
    const gl = renderer.getContext();

    // 2. Set rendering order: mesh before line,
    //    because we want mesh to initialize stencil buffers before line rendering.
    cube.renderOrder = 1;
    line.renderOrder = 2;

    // 3. Provide render callbacks
    cube.onBeforeRender = function() {
        // enable stencil buffer test
        gl.enable(gl.STENCIL_TEST);

        // do it just for all mesh pixels
        gl.stencilFunc(gl.ALWAYS, 1, 0xFF);

        // ... with no masking
        gl.stencilMask(0xFF);

        // ... simply increment stencil buffer value for each draw call,
        // (important, here we have 
        gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
    }
    cube.onAfterRender = function() {
        // nothing to do
    }

    line.onBeforeRender = function() {
        // don't rely on z-Buffer for line, disable depth check
        gl.disable(gl.DEPTH_TEST);

        // enable stencil buffer check instead
        gl.enable(gl.STENCIL_TEST)

        gl.stencilMask(0x00);

        // render line only where stencil buffer was incremented exactly twice
        gl.stencilFunc(gl.EQUAL, 2, 0xFF);
    }

    line.onAfterRender = function() {
        // restore flags to initial order
        gl.disable(gl.STENCIL_TEST);
        gl.enable(gl.DEPTH_TEST);
    }

    // don't let user click the button twice
    document.getElementById("btn").setAttribute("disabled", true);
}
Alex Khoroshylov
  • 2,234
  • 1
  • 17
  • 28
  • 3
    Instead, `polygonOffset` in https://stackoverflow.com/questions/31539130/display-wireframe-and-solid-color/31541369#31541369 works in your case. – WestLangley Mar 20 '19 at 16:29