使用Raycaster

时间:2017-02-25 13:01:37

标签: camera three.js

documentation之后很容易弄清楚如何点击网格,但是防止相机穿过网格并不那么容易。我需要一些指导。

如何使用Raycaster阻止相机穿过混乱?

jsbin

<!DOCTYPE html>
<html lang="en">

<head>
  <title>three.js webgl - interactive cubes</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>

<body style="margin:0;overflow:hidden;">
  <div style="position:fixed;background:rgba(255,255,255,0.9);" onmouseout="new function(){controls=new function(){this.moveX=0;this.moveY=0;this.moveZ=0;this.rotateX=0;this.rotateY=0;};}"><a href="javascript:void(0);" onmousedown="new function(){controls.moveX=1;clickedIn=false;}" onmouseup="new function(){controls.moveX=0;}">Move Right</a><a href="javascript:void(0);" onmousedown="new function(){controls.moveX-=1;clickedIn=false;}" onmouseup="new function(){controls.moveX=0;}">Move Left</a><a href="javascript:void(0);" onmousedown="new function(){controls.moveY=-1;clickedIn=false;}" onmouseup="new function(){controls.moveY=0;}">Move Down</a><a href="javascript:void(0);" onmousedown="new function(){controls.moveY=1;clickedIn=false;}" onmouseup="new function(){controls.moveY=0;}">Move Up</a><a href="javascript:void(0);" onmousedown="new function(){controls.moveZ=1;clickedIn=false;}" onmouseup="new function(){controls.moveZ=0;}">Move Back</a><a href="javascript:void(0);" onmousedown="new function(){controls.moveZ=-1;clickedIn=false;}" onmouseup="new function(){controls.moveZ=0;}">Move Front</a><a href="javascript:void(0);" onmousedown="new function(){controls.rotateY=1;clickedIn=false;}" onmouseup="new function(){controls.rotateY=0;}">Rotate Right</a><a href="javascript:void(0);" onmousedown="new function(){controls.rotateY=-1;clickedIn=false;}" onmouseup="new function(){controls.rotateY=0;}">Rotate Left</a><a href="javascript:void(0);" onmousedown="new function(){controls.rotateX=1;clickedIn=false;}" onmouseup="new function(){controls.rotateX=0;}">Rotate Up</a><a href="javascript:void(0);" onmousedown="new function(){controls.rotateX=-1;clickedIn=false;}" onmouseup="new function(){controls.rotateX=0;}">Rotate Down</a></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
  <script>
    var container;
    var camera, scene, raycaster, renderer;

    var mouse = new THREE.Vector2(),INTERSECTED=[],clickedIn/*bc starts like it was clicked*/=false,controls;

    var clock = new THREE.Clock();

    init();
    animate();

    function init() {

      controls = new function () {
        this.moveX = 0;
        this.moveY = 0;
        this.moveZ = 0;
        this.rotateX = 0;
        this.rotateY = 0;
      }

      container = document.createElement('div');
      document.body.appendChild(container);

      scene = new THREE.Scene();

      camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);

      var light = new THREE.DirectionalLight(0xffffff, 1);
      light.position.set(1, 1, 1).normalize();
      scene.add(light);

      var geometry = new THREE.BoxBufferGeometry(20, 20, 20);

      for (var i = 0; i < 50; i++) {

        var object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff }));

        object.name = 'Index:' + i;
        object.userData.foo = 'foo';

        object.position.x = Math.floor(Math.random() * 201) - 100;
        object.position.y = Math.floor(Math.random() * 201) - 100;
        object.position.z = Math.floor(Math.random() * 201) - 100;

        object.rotation.x = Math.random() * 2 * Math.PI;
        object.rotation.y = Math.random() * 2 * Math.PI;
        object.rotation.z = Math.random() * 2 * Math.PI;

        object.scale.x = Math.random() + 0.5;
        object.scale.y = Math.random() + 0.5;
        object.scale.z = Math.random() + 0.5;

        scene.add(object);

      }


      raycaster = new THREE.Raycaster();

      renderer = new THREE.WebGLRenderer();
      renderer.setClearColor(0xf0f0f0);
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.sortObjects = false;
      container.appendChild(renderer.domElement);

      container.addEventListener('click', function (event) {
        event.preventDefault();
        clickedIn = true;
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
      }, false);

      window.addEventListener('resize', function () {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }, false);

    }

    function animate() {

      requestAnimationFrame(animate);

      // raycaster
      raycaster.setFromCamera(mouse, camera);
      var intersects = raycaster.intersectObjects(scene.children);
      for (var i = 0; i < INTERSECTED.length; i++) {
        INTERSECTED[i].material.emissive.setHex(INTERSECTED[i].currentHex);
        INTERSECTED.splice(i, 1);
      }
      for (var i = 0; clickedIn && i < intersects.length; i++) {
        var length = INTERSECTED.push(intersects[0].object) - 1;
        INTERSECTED.currentHex = INTERSECTED[length].material.emissive.getHex();
        INTERSECTED[length].material.emissive.setHex(0xff0000);
      }

      // move
      var delta = clock.getDelta(), step = 100, stepAngle = (Math.PI / 2);
      if (controls.moveX == 1) camera.translateX(step * delta);
      else if (controls.moveX == -1) camera.translateX(-step * delta);
      if (controls.moveY == 1) camera.translateY(step * delta);
      else if (controls.moveY == -1) camera.translateY(-step * delta);
      if (controls.moveZ == 1) camera.translateZ(step * delta);
      else if (controls.moveZ == -1) camera.translateZ(-step * delta);
      if (controls.rotateX == 1) camera.rotateOnAxis(new THREE.Vector3(1, 0, 0), stepAngle * delta);
      if (controls.rotateX == -1) camera.rotateOnAxis(new THREE.Vector3(1, 0, 0), -stepAngle * delta);
      if (controls.rotateY == 1) camera.rotateOnAxis(new THREE.Vector3(0, 1, 0), stepAngle * delta);
      if (controls.rotateY == -1) camera.rotateOnAxis(new THREE.Vector3(0, 1, 0), -stepAngle * delta);
      camera.updateMatrixWorld();

      // render
      renderer.render(scene, camera);
    }
  </script>

</body>

</html>

1 个答案:

答案 0 :(得分:4)

对于第一人称相机,我认为正确的做法是使用相机的边界球并在场景的每个网格上进行测试,但如果你真的想要使用 raycaster ,那么我可以想到两种方法:

方法1

在渲染循环中:

  • 更新相机的位置;
  • 对于场景中的每个对象:
    • 创建从相机到网格的光线,然后在相机之前稍微开始;
    • 投射光线。如果找到了一个交叉点并位于摄像机前(1),请将摄像机移动到交叉点。

方法2

再次在渲染循环中:

  • 更新相机的位置;
  • 创建一条指向相机方向的光线,然后在相机前稍微开始;
  • 创建指向相机相反方向的另一条光线,然后在相机后稍微开始;
  • 投射第一道光线。如果找到交叉点并且交叉点位于摄像机(1)之前,则将摄像机移动到交叉点;
  • 如果未找到交叉点,则投射第二条光线。如果找到交叉点且交叉点位于摄像机(2)之后,则将摄像机移动到交叉点。

Algorithm cases

第一个算法在 O(n)中, n 是场景中的对象数,而第二个算法在 O(1)< / em>但是对于大网格来说可能很棘手。