ettir_deul
ettir_deul

Reputation: 37

Problem with mouse-camera raycasting in threejs

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

Answers (2)

SalientBrain
SalientBrain

Reputation: 2541

  1. Two results - is OK. You got 2 faces of mesh. Each one intersects ray. Check result intersection properties: face, faceIndex, point.

  2. 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:

https://github.com/brianxu/GPUPicker

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

ThanosSar
ThanosSar

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

Related Questions