Here's a simple demo showing a transparent object that clips an object that is behind it, but only at certain camera angles (try moving your mouse to the left side and the right side, and you'll see the smaller object clips the object that is behind when the mouse is on the right):
const canvas = document.querySelector("#three");
const renderer = new THREE.WebGLRenderer({
canvas
});
renderer.setClearColor("pink");
const camera = new THREE.PerspectiveCamera(75, 2, 10, 1000);
camera.position.z = 2;
const scene = new THREE.Scene();
const loader = new THREE.TextureLoader(new THREE.LoadingManager());
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var mouseX = 0,
mouseY = 0;
const text1 = new THREE.MeshBasicMaterial({
map: loader.load(
"https://upload.wikimedia.org/wikipedia/de/b/bb/Png-logo.png"
)
}); //https://upload.wikimedia.org/wikipedia/commons/9/95/World_map_green.png
text1.transparent = true;
const mesh1 = new THREE.Mesh(new THREE.PlaneBufferGeometry(20, 20), text1);
mesh1.position.z = -50;
mesh1.position.x = -50;
scene.add(mesh1);
const text2 = new THREE.MeshBasicMaterial({
map: loader.load(
"https://upload.wikimedia.org/wikipedia/de/b/bb/Png-logo.png"
)
});
text2.transparent = true;
const mesh2 = new THREE.Mesh(new THREE.BoxBufferGeometry(150, 150, 1), text2);
mesh2.position.z = -60;
scene.add(mesh2);
document.addEventListener("mousemove", onDocumentMouseMove, false);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) * 10;
mouseY = (event.clientY - windowHalfY) * 10;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.position.x = (mouseX - camera.position.x) * 0.005;
camera.position.y = (-mouseY - camera.position.y) * 0.005;
camera.lookAt(mesh2.position);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
#three {
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
<canvas id="three"></canvas>
When the mouse is on the left, it looks fine as expected (as far as I can tell visually, it looks fine, but maybe it still has an issue technically):
When the mouse is on the right, the little PNG image clips the big PNG image that is behind:
The expected result is for the front object to look like it does when mouse is on the left, but at any angle (even when mouse is on the right).
How do we prevent that clipping from appearing.
The solution should not sacrifice critical rendering abilities (f.e., changing default values of depthWrite
or other similar hacks ruins how a typical scene is rendered by default, so that's not an option). A similar question in the Three.js forums is unanswered (except for the hacks that ruin critical features of default rendering).