Shex
Shex

Reputation: 131

ThreeJS raycasting problem with an Orthographic camera in isometric-like view

I'm pulling my hair off this one. I have to work with a specific camera angle for a 3D projet with the constraint of using an Orthographic Camera. I need to be able to precisely click on the floor for gameplay purposes. The ThreeJS Raycast doesn't seem to work properly (or maybe I set something the wrong way?). In a top-down view like angle, it works better.

Here is a fiddle that explains the kind of situation I'm in: https://jsfiddle.net/p6td5oak/42/

const sceneWidth = window.innerWidth;
const sceneHeight = window.innerHeight;
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera( -sceneWidth / 2, sceneWidth / 2, sceneHeight / 2, -sceneHeight / 2, -1000, 1000 );

camera.rotation.set(
    -Math.PI / 12,
  Math.PI / 12,
  Math.PI / 24
);
camera.position.set(0, 1, 0);
camera.zoom = 2;
camera.updateProjectionMatrix();

const renderer = new THREE.WebGLRenderer();
renderer.setSize( sceneWidth, sceneHeight );
document.body.appendChild( renderer.domElement );


const whiteMaterial = new THREE.MeshBasicMaterial({});

const redMaterial = new THREE.MeshBasicMaterial({
  color: 0xFF0000
});

const size = 100;
const geometry = new THREE.PlaneGeometry(size, size, 10, 10);

for (var x = 0; x < 2; x++)
{
        for (var z = 0; z < 2; z++)
    {
                let mesh = new THREE.Mesh(geometry, ((x + z) % 2 ? whiteMaterial : redMaterial));
        
        mesh.rotation.set(
            -Math.PI / 2,
          0,
          0
        );
        mesh.position.set(
            x*size,
          0,
          z*size
        )
        scene.add(mesh);
    }
}


var raycaster = new THREE.Raycaster();
window.addEventListener("pointerup", function(e)
{
    var screenPos = new THREE.Vector2();
  
  screenPos.x = (e.clientX / window.innerWidth) * 2 - 1;
  screenPos.y = - (e.clientY / window.innerHeight) * 2 + 1;
  
    raycaster.setFromCamera(screenPos, camera);
  
  var rays = raycaster.intersectObjects(scene.children, true);
  
    for (var i = 0; i < rays.length; i++)
  {
    scene.remove(rays[i].object);
  }
}
.bind(this));




function animate() {
    requestAnimationFrame( animate );
    renderer.render( scene, camera );
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

In the example, I try to remove the planes as soon as they are clicked. As you can see, the top two planes can be removed if you click around their top-left corner. The other twos cannot even be triggered.

If someone have an idea what's going on, you'll be my hero.

Thanks!

PS: I have basic knowledge of ThreeJS but I'm far from being expert

Upvotes: 2

Views: 871

Answers (1)

Mugen87
Mugen87

Reputation: 31076

Raycaster only detects objects in front of the camera, and your camera is located near the origin. Move the camera back.

Also, the near value of your orthographic camera is invalid. From the documentation:

The valid range is between 0 and the current value of the far plane.

Negative values are not supported.

const sceneWidth = window.innerWidth;
const sceneHeight = window.innerHeight;
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(-sceneWidth / 2, sceneWidth / 2, sceneHeight / 2, -sceneHeight / 2, 0.1, 1000);

camera.rotation.set(
  -Math.PI / 12,
  Math.PI / 12,
  Math.PI / 24
);
camera.position.set(100, 100, 500);
camera.zoom = 2;
camera.updateProjectionMatrix();

const renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(sceneWidth, sceneHeight);
document.body.appendChild(renderer.domElement);

const whiteMaterial = new THREE.MeshBasicMaterial();

const redMaterial = new THREE.MeshBasicMaterial({
  color: 0xFF0000
});

const size = 100;
const geometry = new THREE.PlaneGeometry(size, size, 10, 10);

for (let x = 0; x < 2; x++) {
  for (let z = 0; z < 2; z++) {
    let mesh = new THREE.Mesh(geometry, ((x + z) % 2 ? whiteMaterial : redMaterial));

    mesh.rotation.set(
      -Math.PI / 2,
      0,
      0
    );
    mesh.position.set(
      x * size,
      0,
      z * size
    )
    scene.add(mesh);
  }
}


const raycaster = new THREE.Raycaster();
const screenPos = new THREE.Vector2();

renderer.domElement.addEventListener("pointerup", function(e) {

  screenPos.x = (e.clientX / window.innerWidth) * 2 - 1;
  screenPos.y = -(e.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(screenPos, camera);

  const intersections = raycaster.intersectObject(scene, true);

  for (let i = 0; i < intersections.length; i++) {
    scene.remove(intersections[i].object);
  }
});

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();
body {
    margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>

Upvotes: 2

Related Questions