如何修复 three.js 中网格表面线的 z-fighting?

How to fix z-fighting of the line on mesh surface in three.js?

我正在尝试在 three.js

的脸上画线

一切正常,除了线条几乎看不见 - 无论我把它们做得多厚:它们看起来像这样:

画线的代码是:

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 );

我怀疑 - 由于表面上的线条 完全 ,因此它们不会被渲染(这就是所谓的 z-fighting 吗?)

有办法解决吗?

我正在考虑:

有什么搬进来的建议或方向吗?

这可以通过层和模板缓冲区的组合来实现。

工作演示: https://jsfiddle.net/mmalex/dg417kvn/


解法:

解释请看下面代码中的注释:

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);
}