微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Three.js 使用 Raycaster 检测 ArrowHelper 的线和锥子节点

如何解决Three.js 使用 Raycaster 检测 ArrowHelper 的线和锥子节点

我有一个功能强大的 Raycaster,用于一个简单的绘画应用程序。我将它用于“桶工具”,用户可以在其中单击对象并更改其颜色。它适用于 BoxGeometry 和 CircleGeometry 等几何对象,但我正在努力将其应用于 ArrowHelper 对象的子级。由于 ArrowHelper 不是形状并且不具有几何属性,因此 Raycaster 在检查 scene.children 是否有相交时不会检测与其位置的碰撞。然而,ArrowHelper 对象的子对象总是两个东西:直线和圆锥体,它们都具有几何、材质和位置属性

我已经尝试过:

  1. 函数 recursive.intersectObjects(objects: Array,recursive: Boolean,optionalTarget: Array ) 布尔值切换为 true,以便它包括数组中对象的子代。
  2. 通过遍历 ArrowHelper 对象的 scene.children 并将其线和锥体添加到单独的对象数组中来绕过 ArrowHelper 父级。从那里我尝试仅使用线和锥体列表检查交叉点,但没有检测到交叉点。

Raycaster 设置:

  const runRaycaster = (mouseEvent) => {

... // sets mouse and canvas bounds here

    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse,camera);

    const intersects = raycaster.intersectObjects(scene.children,true);

    if (intersects.length > 0) {
      for (let i = 0; i < intersects.length; i++) {
        // works for GEOMETRY ONLY
        // needs modifications for checking ArrowHelpers
        intersects[i].object.material.color.set(currentColor);
      }
    }
  };

这是我在没有 ArrowHelper 父项的情况下单独检查线条和锥体的尝试:

    let arrowObjectsList = [];
      for (let i = 0; i < scene.children.length; i++) {
        if (scene.children[i].type === 'ArrowHelper') {
          arrowObjectsList.push(scene.children[i].line);
          arrowObjectsList.push(scene.children[i].cone);
        } else {
          console.log(scene.children[i].type);
        }
      }
      console.log(arrowObjectsList); // returns 2 objects per arrow on the canvas
    // intersectsArrows always returns empty
    const intersectsArrows = raycaster.intersectObjects(arrowObjectsList,true);

一些注意事项:

  1. 每个 ArrowHelper、它的线和它的锥体都有唯一可识别的名称,以便以后可以重新着色/重新定位/删除
  2. Raycaster 与每个 onMouseDown 和 onMouseMove 事件一起运行。
  3. 值得注意的是,ArrowHelpers 的线和锥子项分别是 BufferGeometry 和 CylinderBufferGeometry,而不是 Geometry 的变体。我想知道这是否与它有关。根据 Three.JS 文档网站的 this example 的说法,Raycaster 可以以类似的方式检测 BufferGeometry。

解决方法

设置 recursion = true 对我有用。运行下面的简单代码,然后单击箭头。您将看到打印到控制台的交叉点信息。 (三.js r125)

let W = window.innerWidth;
let H = window.innerHeight;

const renderer = new THREE.WebGLRenderer({
  antialias: true,alpha: true
});
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(28,1,1000);
camera.position.set(5,5,5);
camera.lookAt(scene.position);
scene.add(camera);

const light = new THREE.DirectionalLight(0xffffff,1);
light.position.set(0,-1);
camera.add(light);

const mesh = new THREE.ArrowHelper(
  new THREE.Vector3(0,1),new THREE.Vector3(0,0),2,0xff0000,1
);
scene.add(mesh);

function render() {
  renderer.render(scene,camera);
}

function resize() {
  W = window.innerWidth;
  H = window.innerHeight;
  renderer.setSize(W,H);
  camera.aspect = W / H;
  camera.updateProjectionMatrix();
  render();
}

window.addEventListener("resize",resize);

resize();
render();

// RAYCASTER STUFF
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

renderer.domElement.addEventListener('mousedown',function(e) {

  mouse.set(
    (event.clientX / window.innerWidth) * 2 - 1,-(event.clientY / window.innerHeight) * 2 + 1
  );

  raycaster.setFromCamera(mouse,camera);

  const intersects = raycaster.intersectObjects(scene.children,true);

  console.log(intersects);

});
html,body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  overflow: hidden;
  background: skyblue;
}
<script src="https://threejs.org/build/three.min.js"></script>

,

仔细检查后,这是设置位置的问题,不一定是箭头。箭头的位置根据用户鼠标点击而变化,以指定起点。但是,它仍然存在几个问题:尽管可以编辑,但 lineWidthLineBasicMaterial 值除了 1 之外不能有任何其他值,因此很难选择该行。这是由于 OpenGL 核心配置文件中的限制,如 the docsthis question 中所述。同样,锥体不会响应 setLength。这严重限制了 ArrowHelper 工具的自定义。

因此,我决定用两个耦合在一起的对象完全替换 ArrowHelper:tubeGeometry 和 coneGeometry,两者都分配了一个 MeshBasicMaterial,以一种可由 Raycasters 开箱即用的方式访问。

... // the pos Float32Array is set according to user mouse coordinates.

  const v1 = new THREE.Vector3(pos[0],pos[1],pos[2]);
  const v2 = new THREE.Vector3(pos[3],pos[4],pos[5]);

  const material = new THREE.MeshBasicMaterial({
    color: color,side: THREE.DoubleSide,});
  // Because there are only two vectors,no actual curve occurs.
  // Therefore,it's our straight line.
  const tubeGeometry = new THREE.TubeBufferGeometry(
      new THREE.CatmullRomCurve3([v1,v2]),3,false);
  const coneGeometry = new THREE.ConeGeometry(10,10,false);
  arrowLine = new THREE.Mesh(tubeGeometry,material);
  arrowTip = new THREE.Mesh(coneGeometry,material);
  // needs names to be updated later.
  arrowLine.name = 'arrowLineName';
  arrowTip.name = 'arrowTipName';

放置箭头时,用户将单击并拖动以指定箭头的起点和终点,因此箭头及其尖端必须更新为 onMouseMove。我们必须使用 Math.atan2 来获得 v1 和 v2 之间的角度,以 v1 为中心。减去 90 会将旋转定向到默认位置。

... // on the onMouseMove event,pos is updated with new coords.

const setDirection = () => {
    const v1 = new THREE.Vector3(pos[0],pos[2]);
    const v2 = new THREE.Vector3(pos[3],pos[5]);
    
    // copying the v2 pos ensures that the arrow tip is always at the end.
    arrowTip.position.copy(v2);

    // rotating the arrow tip according to the angle between start and end
    // points,v1 and v2.
    let angleDegrees = 180 - (Math.atan2(pos[1] - pos[4],pos[3] - pos[0]) * 180 / Math.PI - 90);
    const angleRadians = angleDegrees * Math.PI / 180;
    arrowTip.rotation.set(0,angleRadians);
    
    // NOT VERY EFFICIENT,but it does the job to "update" the curve.
    arrowLine.geometry.copy( new THREE.TubeBufferGeometry(new THREE.CatmullRomCurve3([v1,false));
    scene.add(arrowLine);
    scene.add(arrowTip);
  };

开箱即用,这个“箭头”允许我使用 Raycaster 毫无问题地选择和编辑它。无需担心线条定位、线条粗细或线条长度。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?