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:
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:
Any advice or direction to move in?
This is achievable with combination of layers and stencil buffers.
Working Demo: https://jsfiddle.net/mmalex/dg417kvn/
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);
}