Reputation: 37
I try to make a very simple program with a clickable 3d object in Threejs. My code is based on
https://threejs.org/examples/#webgl_interactive_cubes
It works when I click on the object (although the resulting array contains the object twice, I assume because the ray intersects it when entering it and when exiting it).
But when I click in an area just surrounding the object raycaster.intersectObjects
returns the object although it should return an empty array.
What am I doing wrong? Why is the object also intersected when I click next to it and not on it? And is an object always included twice in the intersect array because of the ray entering and exiting it?
A working example of the code is here:
https://codepen.io/ettir_deul/pen/PoGLNZx
(open the console to see the intersect array after you clicked on the screen)
And the code looks like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="libs/three.min.js.r116.1"></script>
</head>
<body>
<div id="threejsCanvas"></div>
<script>
let scene, center, camera, renderer, raycaster;
const mouse = new THREE.Vector2();
const threejsCanvas = document.getElementById("threejsCanvas");
init3d();
function init3d(){
scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAEE);
center = new THREE.Vector3(0, 0, 0);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(center.x, center.y, center.z+1);
camera.lookAt(center);
buttonMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0.2), new THREE.MeshBasicMaterial({map : null, color: 0x008844, wireframe: false, side: THREE.DoubleSide}));
buttonMesh.position.set(center.x, center.y, center.z);
scene.add(buttonMesh);
buttonMesh.objId = "buttonMesh";
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
threejsCanvas.appendChild(renderer.domElement);
renderer.render(scene, camera);
raycaster = new THREE.Raycaster();
document.addEventListener('click', onMouseClick, false);
}
function onMouseClick(event){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
console.log(intersects);
}
</script>
</body>
</html>
Upvotes: 2
Views: 476
Reputation: 2541
Two results - is OK. You got 2 faces of mesh. Each one intersects ray. Check result intersection properties: face, faceIndex, point.
Raycaster is not precise and pretty slow. You can use its 'params' to change precision (see https://threejs.org/docs/#api/en/core/Raycaster). I suggest using GPUPicker. It is precise and super fast. Check here:
Edit: Yes you can change material to avoid 2 intersections. But it is often it is not acceptable for surfaces.
Edit: Yes precision settings affects only points and line picking. But GPUPicker can pick exact rendering result of points (shader effects, symbols with transparency). But standard raycaster - can't.
Upvotes: 0
Reputation: 542
the reason for getting the object twice is the
side: THREE.DoubleSide
in the material. You can use THREE.FrontSide
The reason for picking the object when the mouse is close is the canvas and window dimensions not being equal. If you add :
body{
padding: 0;
margin: 0;
}
in the CSS it is fixed.
Here is a codepen that works.
Upvotes: 4