如何解决Three.js 使用 Raycaster 检测 ArrowHelper 的线和锥子节点
我有一个功能强大的 Raycaster,用于一个简单的绘画应用程序。我将它用于“桶工具”,用户可以在其中单击对象并更改其颜色。它适用于 BoxGeometry 和 CircleGeometry 等几何对象,但我正在努力将其应用于 ArrowHelper 对象的子级。由于 ArrowHelper 不是形状并且不具有几何属性,因此 Raycaster 在检查 scene.children
是否有相交时不会检测与其位置的碰撞。然而,ArrowHelper 对象的子对象总是两个东西:直线和圆锥体,它们都具有几何、材质和位置属性。
我已经尝试过:
- 将函数
recursive
的.intersectObjects(objects: Array,recursive: Boolean,optionalTarget: Array )
布尔值切换为 true,以便它包括数组中对象的子代。 - 通过遍历 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);
一些注意事项:
- 每个 ArrowHelper、它的线和它的锥体都有唯一可识别的名称,以便以后可以重新着色/重新定位/删除。
- Raycaster 与每个 onMouseDown 和 onMouseMove 事件一起运行。
- 值得注意的是,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>
仔细检查后,这是设置位置的问题,不一定是箭头。箭头的位置根据用户鼠标点击而变化,以指定起点。但是,它仍然存在几个问题:尽管可以编辑,但 lineWidth
的 LineBasicMaterial
值除了 1 之外不能有任何其他值,因此很难选择该行。这是由于 OpenGL 核心配置文件中的限制,如 the docs 和 this 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 举报,一经查实,本站将立刻删除。