Three.js中光线投射Raycaster的简单使用案例 与模型的交互,当鼠标移动到模型时出现信息框

目录

说明 

 创建两个模型

基础代码 

基础代码效果图如下:

重点!!! 

 创建光线投射Raycaster实例步骤

1.准备一个盒子,用来展示模型的长宽高信息,初始化时先隐藏该盒子

2.创建光线投射Raycaster实例

        1.创建 Raycaster 实例 

          2.为窗口绑定事件 pointermove 想使用点击事件 click 的可以自行修改

        3.定义窗口触发 pointermove 事件所执行的回调函数 onPointerMove 

        4.通过摄像机和鼠标位置更新射线

完整代码如下:

效果图如下 :

 结尾


说明 

说明:该案例是基于Vue2创建,如果未使用Ve2请自行修改代码,另外由于使用的是已经下载的Three.js,所以运行前请确保已安装Three.js以方便引入,未安装可以使用 npm install three 进行安装

 创建两个模型

先创建两个基本模型为 光线投射Raycaster 做铺垫

下面是一个名为 model 组件的编写,读者可以自行挂载在Vue示例上  

另外如果有 model 命名带来的错误,可以在文件 vue.config.js 中添加配置 lintOnSave: false

基础代码 

<template>
    <div ref="container">
        
    </div>
</template>

<script>
import * as THREE from "three";
import {OrbitControls} from "three/addons/controls/OrbitControls.js";

export default {
    name: "model",
    data() {
        return {
            //场景
            scene: null,
            //摄影机
            camera: null,
            //渲染器
            renderer: null,
            //相机控件
            controls: null,
        }
    },
    mounted() {
        // 调用方法创建场景、相机、渲染器和相机控件
        this.createScene();
        this.createCamera();
        this.createRenderer();
        this.createControls();

        // 创建两个不同大小的立方体模型,材质使用不受光照影响的 MeshBasicMaterial 材质
        const cube1 = new THREE.Mesh(
            new THREE.BoxGeometry(3, 2, 1),
            new THREE.MeshBasicMaterial({color: 0xff0000})
        );
        const cube2 = new THREE.Mesh(
            new THREE.BoxGeometry(1, 2, 3),
            new THREE.MeshBasicMaterial({color: 0x00ff00})
        );
        cube1.position.set(-2, 0, 0);
        cube2.position.set(2, 0, 0);
        this.scene.add(cube1, cube2);

        const render = () => {
            //手动更改相机的变换后,必须调用controls.update()
            this.controls.update()
            this.renderer.render(this.scene, this.camera);
            requestAnimationFrame(render);
        };
        render();
    },
    methods: {
        //创建场景
        createScene() {
            this.scene = new THREE.Scene();
        },
        //创建相机
        createCamera() {
            this.camera = new THREE.PerspectiveCamera(
                45,
                window.innerWidth / window.innerHeight,
                1,
                1000
            );
            this.camera.position.set(0, 0, 5);
        },
        //创建渲染器
        createRenderer() {
            this.renderer = new THREE.WebGLRenderer({antialias: true}); //antialias:是否执行抗锯齿,默认为false.
            this.renderer.setSize(window.innerWidth, window.innerHeight);
            this.$refs.container.appendChild(this.renderer.domElement);
        },
        //创建相机控件
        createControls() {
            this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        }
    }
};
</script>

<style scoped>

</style>

基础代码效果图如下:


重点!!! 

 创建光线投射Raycaster实例步骤

1.准备一个盒子,用来展示模型的长宽高信息,初始化时先隐藏该盒子

template>
    <div ref="container">
        <div id="infoBox"></div>
    </div>
</template>

//盒子样式如下: (要是觉得盒子丑大家可以自己修改,哈哈)
#infoBox{
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    background-color: #fff;
    border:1px solid #ccc;
    padding: 5px;
}

2.创建光线投射Raycaster实例

为了大家方便对照官方文档学习,所以我直接引用了官文文档的源码,大家可以对照官文修改代码进行实验加深理解 

官网地址:https://threejs.org/docs/index.html#api/zh/core/Raycaster

        1.创建 Raycaster 实例 

const raycaster = new THREE.Raycaster();
 //创建一个二维向量为后面 Raycaster 实例调用 .setFromCamera 方法做准备
const pointer = new THREE.Vector2();

         2.为窗口绑定事件 pointermove 想使用点击事件 click 的可以自行修改

window.addEventListener('pointermove', onPointerMove);

        3.定义窗口触发 pointermove 事件所执行的回调函数 onPointerMove 

const infoBox = document.querySelector('#infoBox') //获取Dom元素
const onPointerMove = (event) => {  //如果不使用箭头函数需要注意this指向问题
    // 修改 pointer 的值:将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
    pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
    //计算物体和射线的焦点
    // 方法 .intersectObjects ( objects : Array, recursive : Boolean, optionalTarget : Array ) : Array
    // 作用:检测所有在射线与这些物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个),
    //      相交部分和.intersectObject所返回的格式是相同的。
    const intersects = raycaster.intersectObjects(this.scene.children);//返回和射线相交的一组物体,值为数组
    //没有相交物体时
    if (intersects.length === 0) {
        console.log('隐藏');
        infoBox.style.display = "none";
        return;
    }
    //有相交物体时
    if (intersects.length > 0) { //其中数组第一个值的 object属性值就是鼠标放在屏幕上离我们最近的模型
        console.log('显示');
        //设置信息
        infoBox.innerHTML = `长:${intersects[0].object.geometry.parameters.depth}
        <br>宽:${intersects[0].object.geometry.parameters.width}<br>
        高:${intersects[0].object.geometry.parameters.height}`;
        infoBox.style.display = "block";
        console.log(event.clientX);
        infoBox.style.left = event.clientX + "px"; //记得一定要拼接 px 我就是开始忘记了,导致信息框不移动
        infoBox.style.top = event.clientY + "px"
    }
}

        4.通过摄像机和鼠标位置更新射线

raycaster.setFromCamera(pointer, this.camera);

以上就是 光线投射Raycaster 的使用步骤


完整代码如下:

<template>
    <div ref="container">
        <div id="infoBox"></div>
    </div>
</template>

<script>
import * as THREE from "three";
import {OrbitControls} from "three/addons/controls/OrbitControls.js";

export default {
    name: "model",
    data() {
        return {
            //场景
            scene: null,
            //摄影机
            camera: null,
            //渲染器
            renderer: null,
            //相机控件
            controls: null,
        }
    },
    mounted() {
        // 调用方法创建场景、相机、渲染器和相机控件
        this.createScene();
        this.createCamera();
        this.createRenderer();
        this.createControls();

        // 创建两个不同大小的立方体模型,材质使用不受光照影响的 MeshBasicMaterial 材质
        const cube1 = new THREE.Mesh(
            new THREE.BoxGeometry(3, 2, 1),
            new THREE.MeshBasicMaterial({color: 0xff0000})
        );
        const cube2 = new THREE.Mesh(
            new THREE.BoxGeometry(1, 2, 3),
            new THREE.MeshBasicMaterial({color: 0x00ff00})
        );
        cube1.position.set(-2, 0, 0);
        cube2.position.set(2, 0, 0);
        this.scene.add(cube1, cube2);

        //1.创建 Raycaster 实例
        const raycaster = new THREE.Raycaster();
        const pointer = new THREE.Vector2(); //创建一个二维向量为后面 Raycaster 实例调用 .setFromCamera 方法做准备
        //3.定义窗口触发 pointermove 事件所执行的回调函数 onPointerMove
        const infoBox = document.querySelector('#infoBox') //获取Dom元素
        const onPointerMove = (event) => {  //如果不使用箭头函数需要注意this指向问题
            // 修改 pointer 的值:将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
            pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
            pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
            //计算物体和射线的焦点
            // 方法 .intersectObjects ( objects : Array, recursive : Boolean, optionalTarget : Array ) : Array
            // 作用:检测所有在射线与这些物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个),
            //      相交部分和.intersectObject所返回的格式是相同的。
            const intersects = raycaster.intersectObjects(this.scene.children);//返回和射线相交的一组物体,值为数组
            //没有相交物体时
            if (intersects.length === 0) {
                console.log('隐藏');
                infoBox.style.display = "none";
                return;
            }
            //有相交物体时
            if (intersects.length > 0) { //其中数组第一个值的 object属性值就是鼠标放在屏幕上离我们最近的模型
                console.log('显示');
                //设置信息
                infoBox.innerHTML = `长:${intersects[0].object.geometry.parameters.depth}
        <br>宽:${intersects[0].object.geometry.parameters.width}<br>
        高:${intersects[0].object.geometry.parameters.height}`;
                infoBox.style.display = "block";
                console.log(event.clientX);
                infoBox.style.left = event.clientX + "px"; //记得一定要拼接 px 我就是开始忘记了,导致信息框不移动
                infoBox.style.top = event.clientY + "px"
            }
        }
        //2.为窗口绑定事件 pointermove 想使用点击事件 click 的可以自行修改
        window.addEventListener('pointermove', onPointerMove);

        const render = () => {
            // 4.通过摄像机和鼠标位置更新射线
            raycaster.setFromCamera(pointer, this.camera);
            //手动更改相机的变换后,必须调用controls.update()
            this.controls.update()
            this.renderer.render(this.scene, this.camera);
            requestAnimationFrame(render);
        };
        render();
    },
    methods: {
        //创建场景
        createScene() {
            this.scene = new THREE.Scene();
        },
        //创建相机
        createCamera() {
            this.camera = new THREE.PerspectiveCamera(
                45,
                window.innerWidth / window.innerHeight,
                1,
                1000
            );
            this.camera.position.set(0, 0, 5);
        },
        //创建渲染器
        createRenderer() {
            this.renderer = new THREE.WebGLRenderer({antialias: true}); //antialias:是否执行抗锯齿,默认为false.
            this.renderer.setSize(window.innerWidth, window.innerHeight);
            this.$refs.container.appendChild(this.renderer.domElement);
        },
        //创建相机控件
        createControls() {
            this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        }
    }
};
</script>

<style scoped>
#infoBox {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    background-color: #fff;
    border: 1px solid #ccc;
    padding: 5px;
}
</style>

效果图如下 :


 结尾

要是读者觉得帮到你们了,麻烦点个赞鼓励一下,以便鼓舞我这个新手小白,谢谢大家

另外大家要有什么疑问或者是指教都可以在评论区发出来,作者看到一定回复,谢谢大家

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值