【问题标题】:Three.js: Raycast Has an Empty Array of IntersectsThree.js:Raycast 有一个空的相交数组
【发布时间】:2017-09-07 00:48:37
【问题描述】:

我将这个示例用于我的 WebGL 全景立方体:https://threejs.org/examples/?q=pano#webgl_panorama_equirectangular

我想知道立方体用户点击了什么,我发现我可以为此使用 Raycaster。根据文档,我添加了以下功能:

    function onMouseDown( event ) {
        event.preventDefault();

        var mouseVector = new THREE.Vector3(
            ( event.clientX / window.innerWidth ) * 2 - 1,
          - ( event.clientY / window.innerHeight ) * 2 + 1,
            1 );

        //projector.unprojectVector( mouseVector, camera );
        mouseVector.unproject( camera );
        var raycaster = new THREE.Raycaster( camera.position, mouseVector.sub( camera.position ).normalize() );

        // create an array containing all objects in the scene with which the ray intersects
        var intersects = raycaster.intersectObjects( scene.children );
        console.log(intersects);
        if (intersects.length>0){
            console.log("Intersected object:", intersects.length);
            intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
        }
        // ...

intersects 始终为空。我的场景定义为

scene = new THREE.Scene();

并添加了天空盒:

var skyBox = new THREE.Mesh( new THREE.CubeGeometry( 1, 1, 1 ), materials );
skyBox.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, - 1 ) );
scene.add( skyBox );

我看过与此问题相关的类似帖子,但不知道如何应用于此示例。任何方向表示赞赏。

【问题讨论】:

    标签: javascript three.js


    【解决方案1】:

    尝试将此添加到您的材质定义中:

    var materials = new THREE.SomeMaterial({
        /* other settings */,
        side: THREE.DoubleSide
    });
    

    Raycaster 不会与背面相交,除非 side 属性设置为 THREE.BackSideTHREE.DoubleSide。即使您的缩放在技术上颠倒了面部方向,顶点顺序保持不变,这对Raycaster 很重要。

    一些进一步的解释

    下面的 sn-p 显示了从相机投射到天空盒中心的射线,该天空盒中心被 -Z 比例反转。

    盒子本身看起来很奇怪,因为它已经过 -Z 缩放,并且法线不再与材质匹配。但那不是这里也不是那里。

    绿色箭头代表原始射线。红色箭头表示Mesh.raycast 函数内该光线会发生什么,它将对象世界矩阵的逆矩阵应用于光线,但不应用于对象的几何体。这是一个完全不同的问题。

    我要说的是,在Mesh.raycast 内,它不会影响顶点/索引顺序,因此当它检查网格的三角形时,它们仍然是原始顺序。对于标准的BoxGeometry/BoxBufferGeometry,这意味着所有面都从几何原点向外。

    这意味着光线(不管变换矩阵如何影响它们)仍在尝试与这些三角形的背面相交,除非材质设置为 THREE.DoubleSide,否则这将不起作用。 (也可以设置为THREE.BackSide,但-Z 比例会破坏它。)

    如果 -Z 缩放框未设置为 THREE.DoubleSide(默认),则单击任一光线投射按钮将产生 0 个相交。单击“设置 THREE.DoubleSide”按钮并再试一次 - 现在它会相交。

    var renderer, scene, camera, controls, stats;
    
    var WIDTH = window.innerWidth,
    	HEIGHT = window.innerHeight,
    	FOV = 35,
    	NEAR = 1,
    	FAR = 1000,
      ray1, ray2, mesh;
    
    function populateScene(){
    	var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
    		cubeMat = new THREE.MeshPhongMaterial({ color: "red", transparent: true, opacity: 0.5 });
    	mesh = new THREE.Mesh(cubeGeo, cubeMat);
      mesh.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, -1 ) );
      mesh.updateMatrixWorld(true);
    	scene.add(mesh);
      
      var dir = new THREE.Vector3(0.5, 0.5, 1);
      dir.normalize();
      ray1 = new THREE.Ray(new THREE.Vector3(), dir);
      
      var arrow1 = new THREE.ArrowHelper(ray1.direction, ray1.origin, 20, 0x00ff00);
      scene.add(arrow1);
      
      var inverseMatrix = new THREE.Matrix4();
      inverseMatrix.getInverse(mesh.matrixWorld);
      
      ray2 = ray1.clone();
      ray2.applyMatrix4(inverseMatrix);
      
      var arrow2 = new THREE.ArrowHelper(ray2.direction, ray2.origin, 20, 0xff0000);
      scene.add(arrow2);
    }
    
    function init() {
    	document.body.style.backgroundColor = "slateGray";
    
    	renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    
    	document.body.appendChild(renderer.domElement);
    	document.body.style.overflow = "hidden";
    	document.body.style.margin = "0";
    	document.body.style.padding = "0";
    
    	scene = new THREE.Scene();
    
    	camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
    	camera.position.z = 50;
    	scene.add(camera);
    
    	controls = new THREE.TrackballControls(camera, renderer.domElement);
    	controls.dynamicDampingFactor = 0.5;
    	controls.rotateSpeed = 3;
    
    	var light = new THREE.PointLight(0xffffff, 1, Infinity);
    	camera.add(light);
    
    	stats = new Stats();
    	stats.domElement.style.position = 'absolute';
    	stats.domElement.style.top = '0';
    	document.body.appendChild(stats.domElement);
    
    	resize();
    	window.onresize = resize;
    
    	populateScene();
    
    	animate();
      
      var rayCaster = new THREE.Raycaster();
      document.getElementById("greenCast").addEventListener("click", function(){
        rayCaster.ray.copy(ray1);
        alert(rayCaster.intersectObject(mesh).length + " intersections!");
      });
      document.getElementById("redCast").addEventListener("click", function(){
        rayCaster.ray.copy(ray2);
        alert(rayCaster.intersectObject(mesh).length + " intersections!");
      });
      document.getElementById("setSide").addEventListener("click", function(){
        mesh.material.side = THREE.DoubleSide;
        mesh.material.needsUpdate = true;
      });
    }
    
    function resize() {
    	WIDTH = window.innerWidth;
    	HEIGHT = window.innerHeight;
    	if (renderer && camera && controls) {
    		renderer.setSize(WIDTH, HEIGHT);
    		camera.aspect = WIDTH / HEIGHT;
    		camera.updateProjectionMatrix();
    		controls.handleResize();
    	}
    }
    
    function render() {
    	renderer.render(scene, camera);
    }
    
    function animate() {
    	requestAnimationFrame(animate);
    	render();
    	controls.update();
    	stats.update();
    }
    
    function threeReady() {
    	init();
    }
    
    (function () {
    	function addScript(url, callback) {
    		callback = callback || function () { };
    		var script = document.createElement("script");
    		script.addEventListener("load", callback);
    		script.setAttribute("src", url);
    		document.head.appendChild(script);
    	}
    
    	addScript("https://threejs.org/build/three.js", function () {
    		addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
    			addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
    				threeReady();
    			})
    		})
    	})
    })();
    body{
      text-align: center;
    }
    <input id="greenCast" type="button" value="Cast Green">
    <input id="redCast" type="button" value="Cast Red">
    <input id="setSide" type="button" value="Set THREE.DoubleSide">

    【讨论】:

      【解决方案2】:

      您实际上可能希望使用更简单的过程来确定来自相机的光线:

      THREE.Raycaster.prototype.setFromCamera( Vector2, Camera );
      

      只需像在Vector2 中一样定义鼠标坐标,然后将元素传递给 Raycaster 并让它完成它的工作。它隐藏了截锥体与来自相机的光线相交的复杂性,应该可以解决您的问题。

      (此外,光线投射器确实只与直接面对光线的面相交,但由于您的 SkyBox 已反转,它的几何面指向盒子的内部,因此如果相机在盒子内,它们应该相交。另一种可能性是您的盒子比光线投射器默认的far 值更远。)

      function onMouseDown( event ) {
      
          event.preventDefault();
      
          var mouseVector = new THREE.Vector2(
               event.clientX / window.innerWidth * 2 - 1,
              -event.clientY / window.innerHeight * 2 + 1
          );
      
          var raycaster = new THREE.Raycaster;
          raycaster.setFromCamera( mouseVector, camera );
      
          var intersects = raycaster.intersectObjects( scene.children );
              
          console.log(intersects);
              
          if( intersects.length > 0 ){
      
              console.log( "Intersected object:", intersects[ 0 ] );
              intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
      
          }
      
      }

      【讨论】:

      • FWIW,Raycaster.far 默认为Infinity,所以没有问题。 :)
      • 谢谢@somethinghere,你的回答也帮助我理解了
      猜你喜欢
      • 2013-08-18
      • 2012-12-17
      • 1970-01-01
      • 2021-03-11
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      • 2019-12-29
      • 2023-03-14
      相关资源
      最近更新 更多