ThreeJS教程:射线拾取层级模型(模型描边)

推荐:将NSDT场景编辑器加入你的3D工具链
3D工具集:NSDT简石数字孪生

射线拾取层级模型(模型描边)

前面几节课演示过,通过射线投射器Raycaster.intersectObjects()方法可以拾取Mesh模型对象。

如果一个层级模型包含多个网格网格模型Mesh,使用.intersectObjects()方法拾取的时候,返回的结果默认是层级模型的后代Mesh,没办法整体选中该层级模型。

下面以案例:射线拾取工厂设备添加发光描边,给大家演示如何解决含多Mesh的层级模型如何拾取。

.intersectObjects()参数的元素是层级模型

通过Blender查看工厂模型,你可以发现存储罐cunchu.children里面有两个子对象设备A设备B,这些子对象本身都是由多个Mesh构成的父对象。

执行.intersectObjects(cunchu.children)进行射线拾取计算,返回结果并不是设备A设备B父对象,而是他们的某个子对象Mesh。

const cunchu = model.getObjectByName('存储罐');
// 射线拾取模型对象(包含多个Mesh)
// 射线交叉计算拾取模型
const intersects = raycaster.intersectObjects(cunchu.children);

课件源码已经设置好后处理OutlinePass,为了方便测试查看,那个Mesh被选中了,你直接给射线选中模型添加发光描边即可。

outlinePass.selectedObjects = [intersects[0].object];

.intersectObjects()拾取层级模型解决方法

.intersectObjects([父对象A,父对象B...])

给需要射线拾取父对象的所有子对象Mesh自定义一个属性.ancestors,然后让该属性指向需要射线拾取父对象。

const cunchu = model.getObjectByName('存储罐');
// 射线拾取模型对象(包含多个Mesh)
// 可以给待选对象的所有子孙后代Mesh,设置一个祖先属性ancestors,值指向祖先(待选对象)    
for (let i = 0; i < cunchu.children.length; i++) {
    const group = cunchu.children[i];
    //递归遍历chooseObj,并给chooseObj的所有子孙后代设置一个ancestors属性指向自己
    group.traverse(function (obj) {
        if (obj.isMesh) {
            obj.ancestors = group;
        }
    })
}
// 射线交叉计算拾取模型
const intersects = raycaster.intersectObjects(cunchu.children);
console.log('intersects', intersects);
if (intersects.length > 0) {
    // 通过.ancestors属性判断那个模型对象被选中了
    outlinePass.selectedObjects = [intersects[0].object.ancestors];
}

完整代码

鼠标单击选中工厂某个设备,并添加高亮发光描边后处理效果。

addEventListener('click', function (event) {
    const px = event.offsetX;
    const py = event.offsetY;
    //屏幕坐标转标准设备坐标
    const x = (px / window.innerWidth) * 2 - 1;
    const y = -(py / window.innerHeight) * 2 + 1;
    const raycaster = new THREE.Raycaster();
    //.setFromCamera()在点击位置生成raycaster的射线ray
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
    const cunchu = model.getObjectByName('存储罐');
    // 射线拾取模型对象(包含多个Mesh)
    // 可以给待选对象的所有子孙后代Mesh,设置一个祖先属性ancestors,值指向祖先(待选对象)    
    for (let i = 0; i < cunchu.children.length; i++) {
        const group = cunchu.children[i];
        //递归遍历chooseObj,并给chooseObj的所有子孙后代设置一个ancestors属性指向自己
        group.traverse(function (obj) {
            if (obj.isMesh) {
                obj.ancestors = group;
            }
        })
    }
    // 射线交叉计算拾取模型
    const intersects = raycaster.intersectObjects(cunchu.children);
    console.log('intersects', intersects);
    if (intersects.length > 0) {
        // 通过.ancestors属性判断那个模型对象被选中了
        outlinePass.selectedObjects = [intersects[0].object.ancestors];
    }
})

上一篇:ThreeJS教程:Canvas尺寸变化(射线坐标计算) (mvrlink.com)

下一篇:ThreeJS教程:射线拾取Sprite控制场景 (mvrlink.com)

NSDT场景编辑器 | NSDT 数字孪生 | GLTF在线编辑器 | 3D模型在线转换 | UnrealSynth虚幻合成数据生成器 | 3D模型自动纹理化工具
2023 power by nsdt©鄂ICP备2023000829号