threejs 射线拾取函数

threejs 射线拾取函数

将鼠标的屏幕坐标转标准设备坐标

Three.js Canvas画布具有一个标准设备坐标系,该坐标系的坐标原点在canvas画布的中间位置,x轴水平向右,y轴竖直向上。
标准设备坐标系的坐标值不是绝对值,是相对值,范围是[-1,1]区间,也是说canvas画布上任何一个位置的坐标,如果用标准设备坐标系去衡量,那么坐标的所有值都在-1到1之间。

鼠标事件对象event中.offsetX、.offsetY表示鼠标单击位置的坐标,单位是像素px,是以点击的HTML元素左上角为坐标原点,水平向右方向为x轴,竖直向下方向为y轴。

把.offsetX和.offsetY坐标转化为标准设备坐标坐标。canvas画布的宽度是width,.offsetX的范围是0 ~ width,.offsetX除以canvas画布宽度width,就可以从绝对值变成相对值,范围是0~ 1,相对值乘以2,范围0~ 2,再减去1,范围是-1~ 1,刚好和canvas画布标准设备坐标的范围-1~1能够对应起来。

对于.offsetY的转标准设备坐标y,和.offsetX转标准设备坐标x相似,唯一要注意地方就是两个坐标系的y坐标相反,同样计算方式,最后取相反数即可。

有时候也可以使用.clientX、.clientY来计算坐标,这时需要将.clientX、.clientY转化为以canvas画布左上角为原点的坐标。
canvas尺寸发生变化,每次鼠标点击射线拾取都要重新计算canvas的宽高

import * as THREE from "three";
import config from "./config";
/**
 * 通用射线拾取函数,对group和mesh的选择都是有效的
 * @param {*} event 鼠标事件
 * @param {*} chooseObjArr 需要射线拾取的模型对象构成的数组,比如整个场景scene.children
 * @param {*} camera 相机
 * @param {*} isGroup 拾取父对象还是mesh
 * @returns 
 */
function raychoose(event, chooseObjArr, camera, isGroup = false) {
  const Sx = event.clientX; //鼠标单击位置横坐标
  const Sy = event.clientY; //鼠标单击位置纵坐标
  //屏幕坐标转WebGL标准设备坐标
  const x = (Sx / config.cWidth) * 2 - 1; //WebGL标准设备横坐标
  const y = -(Sy / config.cHeight) * 2 + 1; //WebGL标准设备纵坐标
  //创建一个射线投射器`Raycaster`
  const raycaster = new THREE.Raycaster();
  //通过鼠标单击位置标准设备坐标和相机参数计算射线投射器`Raycaster`的射线属性.ray
  raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
  //返回.intersectObjects()参数中射线选中的网格模型对象
  // 未选中对象返回空数组[],选中一个数组1个元素,选中两个数组两个元素
  const intersects = raycaster.intersectObjects(chooseObjArr);
  // console.log("射线器返回的对象", intersects);
  // intersects.length大于0说明,说明选中了模型
  let choose = null;
  if (isGroup) {
    // 射线拾取组group或Object3D方法:
    // 1. intersectObject或intersectObjects的参数2,设置true,计算组的所有后代mesh
    // 2.1.如果要拾取的对象所有后代只有1个层级,可以通过拾取的mesh的父对象名字,判断那个组对象倍选中了
    // 2.2.如果要拾取的对象所有后代只有1个以上层级,可以给对象所有子孙后代mesh,设置一个祖先father属性指向祖先
    for (let i = 0; i < chooseObjArr.length; i++) {
      const group = chooseObjArr[i];
      //递归遍历chooseObj,并给chooseObj的所有子孙后代设置一个ancestors属性指向自己
      group.traverse(function (obj) {
        obj.ancestors = group;
      });
    }
  }
  if (intersects.length > 0) {
    choose = isGroup ? intersects[0].object.ancestors : intersects[0].object;
  }
  return choose;
}

export { raychoose };
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值