Threejs 通过Raycast实现点击效果

Threejs 通过Raycast实现点击效果

引言

Threejs中,要想实现3D物体的点击效果,可以通过射线(Raycast)获取到当前点击到的所有物体。我是个threejs新手,如果写的有不对的地方,希望大家批评指正。

实现

1、通过Raycast获取所有物体

在网上有很多这个例子,官网上也有相应的例子,这里就不多赘述了。

光线投射者 – 三个.js文档 (threejs.org)

const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

/**
  * 获取点击到的所有物体 
  */
function getIntersects(event, group) {
    
    
    event.preventDefault();
    // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    var x = ( event.clientX / window.innerWidth ) * 2 - 1;
    var y = - ( event.clientY / window.innerHeight ) * 2 + 1;
    // 通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线的位置
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
    // 获取与raycaster射线相交的数组集合,其中的元素按照距离排序,越靠近的越靠前
    var intersects = raycaster.intersectObjects(scene, true);
    // 返回选中的对象数组
    return intersects;
}

/**  
  * 点击事件 
  **/
function mouseClick(event) {
    
    
    const intersects = getIntersects(event, deviceGroup);
    if (intersects.length > 0) {
    
    
        //点击到的第一个物体
        let selectObj = intersects[0].object;
        console.log(selectObj);
    }
}

//注册事件,也可以放到div上,根据业务需求来定
window.addEventListener( 'click', mouseClick );

2、隐藏物体也可以点击

经过测试,通过visible = false隐藏物体后,物体还是可以被点击,我用的r146,不知道是我用的有问题,还是怎么的,隐藏之后的物体还是会被点击上,所以这里我们就得处理这个问题。我的解决方案是,获取到所有的点击物体后,来过滤隐藏的物体。

判断物体是否在场景中隐藏。


/**
 * 物体是否在场景中显示 
 * @param {THREE.Object3D} obj 3D物体
 * @return {Boolean} 是否在场景中显示
 * */
function ActiveInHierarchy(obj) {
    
    
    if (!obj.visible) {
    
    
        return false;
    }
    //遍历父对象
    let parent = obj.parent;
    while (parent) {
    
    
        if (!parent.visible) {
    
    
            return false;
        }
        parent = parent.parent;
    }
    return true;
}

3、canvas并非全屏

canvas不是全屏的时候,用以上方法会造成点击偏移的情况,明明就在点击物体又没点击上。


/**
 * 获取点击到的所有物体
 * @param {THREE.Event} event 点击事件
 * @param {THREE.Camera} camera 摄像机
 * @param {THREE.Group} group group
 * @param {Boolean} visible 物体隐藏还是显示
 * @return {Array} 射线下的所有物体
 *  */
function GetIntersects(event, camera, group, visible) {
    
    
    let canvas = event.path[0];
    event.preventDefault();
    /**
     * THREE.Raycaster 对象从屏幕上的点击位置向场景发射的一束光线
     *  THREE.Raycaster,THREE.Vector2():声明raycaster 和mouse变量
     * */
    var raycaster = new THREE.Raycaster();
    //处理画布与电脑屏幕存在偏移量问题
    var divObj = canvas.getBoundingClientRect();
    // 获取鼠标点击位置
    var Sx = event.clientX - divObj.left;
    var Sy = event.clientY - divObj.top;
    // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    var x = (Sx / canvas.width) * 2 - 1;
    var y = -(Sy / canvas.height) * 2 + 1;
    // 通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线的位置
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
    // 获取与raycaster射线相交的数组集合,其中的元素按照距离排序,越靠近的越靠前
    var intersects = raycaster.intersectObjects(group.children, true);
    let objs = [];
    intersects.forEach(object => {
    
    
        if (ActiveInHierarchy(object.object) == visible) {
    
    
            objs.push(object);
        }
    });
    // 返回选中的对象数组query
    return objs;
}

猜你喜欢

转载自blog.csdn.net/yr1102358773/article/details/127811365
今日推荐