Threejs_10 光线投射技术完成画布三维事件交互

你完成了一个threejs的模型之后,里面有很多东西,你咋知道你点击的是哪个呢??如何触发你点击的事件呢?再canvas画布中可不能和html事件一样直接使用e.target来完成了哦。如何做到呢?

光线投射实现三维定位

方法思想

方法就是从点击的地方创造一条虚拟的从相机射入的射线,来计算这条射线是否穿过了什么几何体,穿过了几个,然后通过方法获取到穿过物体的属性。

1.创建三个小球,将其放入场景中

// 创建三个球
const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x00ff00,})
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x0000ff,})
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0xff00ff,})
);
sphere3.position.x = 4;
scene.add(sphere3);

 如果觉得三个球离得太近 可以把相机的z轴位置调高一点  也就是我们的眼睛靠后一点

camera.position.z = 15;
// 为了看到z轴
camera.position.y = 2;
// 设置x轴
camera.position.x = 2;
//设置相机的焦点 (相机看向哪个点)
camera.lookAt(0, 0, 0);

2.新建射线,新建鼠标向量

// 创建射线
const raycaster = new THREE.Raycaster();//创建鼠标向量
const mouse = new THREE.Vector2();

使用THREE.Raycaster 方法创建一个射线,鼠标向量用来存储后面得到的坐标值

3. window事件 获取点击到的地方

window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值
});

4.公式计算出对应的坐标值 传入鼠标向量中

//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);
});

 公式是为什么可以用四个点测试一下,然后自己思考一下。或者直接套用。得到的x,y的值就是平面像素转换为三维坐标的x,y值。

5.通过鼠标向量更新射线坐标

window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);
});

6. 使用raycaster.intersectObjects方法计算物体和射线的焦点

window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);//计算物体和射线的焦点const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);console.log(intersects);
});

点击空的地方得到的就是一个空数组,点击有球的地方,得到的就是一个数组,数组值中有object属性,代表的就是这个小球的内容,可以通过object属性来设置小球的参数。

7.交互Demo实现

当然 也有可能出现两个的情况,所以说我们这时候只用取数组第一个值,用它来设置就好了。我们现在用他来做一个点击就变成红色的交互效果,要求是第二次点击的时候 要变回原有的颜色。

我们的思路就是需要再点击的时候判断他是否已经变过色了,如果没有变过色,就让他变色,并且将现在的颜色存起来。如果变过色,就让他的颜色变回来。

//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);//计算物体和射线的焦点const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);// console.log(intersects);if (intersects[0].object._isSelect) {//设定旧的颜色intersects[0].object.material.color.set(intersects[0].object._originColor);intersects[0].object._isSelect = false;return;}//添加一个属性 选中状态intersects[0].object._isSelect = true;//将之前的颜色记录下来intersects[0].object._originColor =intersects[0].object.material.color.getHex();//设定新的颜色intersects[0].object.material.color.set(0xff0000);
});

 全部代码

//导入 threejs
import * as THREE from "three";
//导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(45, // 视角window.innerWidth / window.innerHeight, // 宽高比 窗口的宽高进行设置的0.1, // 近平面   相机最近最近能看到的物体1000 // 远平面   相机最远能看到的物体
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染器的大小  (窗口大小)
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的dom元素添加到body中
document.body.appendChild(renderer.domElement);
camera.position.z = 15;
// 为了看到z轴
camera.position.y = 2;
// 设置x轴
camera.position.x = 2;
//设置相机的焦点 (相机看向哪个点)
camera.lookAt(0, 0, 0);//添加世界坐标辅助器  (红色x轴,绿色y轴,蓝色z轴)一个线段 参数为 线段长度
const axesHelper = new THREE.AxesHelper(5);
//添加到场景之中
scene.add(axesHelper);// 添加轨道控制器 (修改侦听位置)  一般监听画布的事件  不监听document.body
const controls = new OrbitControls(camera, renderer.domElement);//渲染函数
function animate() {controls.update();//请求动画帧requestAnimationFrame(animate);//渲染renderer.render(scene, camera);
}
animate();
//渲染// 监听窗口的变化 重新设置渲染器的大小 画布自适应窗口
window.addEventListener("resize", () => {// 重新设置渲染器的大小renderer.setSize(window.innerWidth, window.innerHeight);// 重新设置相机的宽高比camera.aspect = window.innerWidth / window.innerHeight;// 重新计算相机的投影矩阵camera.updateProjectionMatrix();
});// 创建三个球
const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x00ff00,})
);
sphere1.position.x = -4;
scene.add(sphere1);
const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0x0000ff,})
);
scene.add(sphere2);
const sphere3 = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32),new THREE.MeshBasicMaterial({color: 0xff00ff,})
);
sphere3.position.x = 4;
scene.add(sphere3);// 创建射线
const raycaster = new THREE.Raycaster();//创建鼠标向量
const mouse = new THREE.Vector2();//给window侦听点击事件 获取点击到的地方
window.addEventListener("click", (e) => {console.log(e.clientX, e.clientY);//设置鼠标向量的xy值//设置鼠标向量的xy值//公式 可以用边框四个点做测试 或者暂时先记住mouse.x = (e.clientX / window.innerWidth) * 2 - 1;mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//通过摄像机和鼠标位置更新射线raycaster.setFromCamera(mouse, camera);//计算物体和射线的焦点const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);// console.log(intersects);if (intersects[0].object._isSelect) {//设定旧的颜色intersects[0].object.material.color.set(intersects[0].object._originColor);intersects[0].object._isSelect = false;return;}//添加一个属性 选中状态intersects[0].object._isSelect = true;//将之前的颜色记录下来intersects[0].object._originColor =intersects[0].object.material.color.getHex();//设定新的颜色intersects[0].object.material.color.set(0xff0000);
});// //给window侦听点击事件 获取点击到的地方
// window.addEventListener("click", (e) => {
//   // console.log(e.clientX, e.clientY);
//   //设置鼠标向量的xy值
//   //公式 可以用边框四个点做测试 或者暂时先记住
//   mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
//   mouse.y = -((e.clientY / window.innerHeight) * 2 - 1);//   //测试向量坐标是否正确
//   // console.log(mouse.x, mouse.y);//   //通过摄像机和鼠标位置更新射线
//   raycaster.setFromCamera(mouse, camera);//   //计算物体和射线的焦点
//   const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);//   if (intersects[0].object._isSelect) {
//     //设定旧的颜色
//     intersects[0].object.material.color.set(intersects[0].object._originColor);
//     intersects[0].object._isSelect = false;
//     return;
//   }
//   //添加一个属性 选中状态
//   intersects[0].object._isSelect = true;
//   //将之前的颜色记录下来
//   intersects[0].object._originColor =
//     intersects[0].object.material.color.getHex();
//   //设定新的颜色
//   intersects[0].object.material.color.set(0xff0000);
//   // console.log(intersects);
// });
//3D 光线投射技术 完成3D三维场景中事件交互

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/154996.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

查看当前目录文件大小的命令(不含子文件)

1、查看当前目录下文件的大小命令: du -sh * 举例: /root/user下面的文件有:bak(10G)、adress(5G) 、name(3G) 三个文件架 使用:du -sh * 结果: 10G bak 5…

自动化物流运输设备模组要选择哪种类型?

在自动化物流运输设备中,选择合适的模组类型取决于具体的运输需求和应用场景。 1、同步带模组:同步带模组是一种低噪音、低成本的物流运输设备,适用于中短距离、轻型货物的运输。它采用同步带传动的方式,具有传动准确、运行稳定、…

安卓老项目改造为AndroidX

由于涉及到了包名的改动,如果从Android Support Library升级到AndroidX需要手动去改每一个文件的包名,那可真得要改死了。(*是的我改完才发现这个办法,给我改到深呼吸,气人! *) 为此&#xff0c…

12 分布式锁加入看门狗

1、看门狗的流程图 2、看门狗的代码实现 /****类说明:Redis的key-value结构*/ public class LockItem {private final String key;private final String value;public LockItem(String key, String value) {this.key key;this.value value;}public String getKey…

CPU架构

CPU架构是指微处理器的设计和实现方式,它决定了处理器的基本运行原理和性能特性。不同的架构适用于不同的应用场景,下面是一些主要的CPU架构类型及其对应的产品: 1. x86/x86-64 架构 描述:最初由 Intel 开发的 x86 架构是目前个…

消消乐游戏开发,消除类游戏

消除游戏是一类简单而又充满乐趣的休闲游戏,通过匹配相同的元素来完成任务,其简单直观的玩法吸引了大量玩家。本文将为你介绍设计和开发一款成功的消除游戏的关键步骤。 1. 确定核心玩法机制 消除游戏的核心在于匹配相同的元素。首先,明确定…

uniapp自定义导航栏返回按键

目录 htmljscss最后 html <!-- #ifdef MP-WEIXIN || APP-PLUS --> <view class"tc header":style"topBarHeight() 0 ? : height: topBarHeight() px;padding-top: topBarTop() px"><view class"reg180" :style"top…

MyBatis Generator 插件 详解自动生成代码

MyBatis Generator&#xff08;MBG&#xff09;是MyBatis和iBATIS的代码生成器。可以生成简单CRUD操作的XML配置文件、Mapper文件(DAO接口)、实体类。实际开发中能够有效减少程序员的工作量&#xff0c;甚至不用程序员手动写sql。 它将为所有版本的MyBatis以及版本2.2.0之后的i…

YOLOV5 C++部署的人员检测项目【学习笔记(十一)】

本文为修改后的转载&#xff0c;没有转载链接&#xff0c;所以文章类型暂为原创 文章目录 一、安装Pytorch 及 YOLO v51.1 安装GPU版 pytorch1.2 安装YOLO v5所需依赖 二、YOLO v5训练自定义数据2.1 标注数据2.1.1 安装labelImg2.1.2 标注 2.2 准备数据集2.2.1 组织目录结构2.…

Python所有常见功能大汇总

以下是Python中常见的语句及其功能。 语句功能import引入模块from…import从某个模块引入指定内容def定义函数if/else条件语句elif多条件判断while循环语句for循环语句break终止循环continue跳过当前循环return指定返回值try/except异常处理with简化文件操作open打开文件read/…

深度学习之三(卷积神经网络--Convolutional Neural Networks,CNNs)

概念 卷积神经网络(Convolutional Neural Networks,CNNs)是一种特殊的神经网络结构,专门用于处理具有网格状结构(如图像、音频)的数据。CNN 在计算机视觉领域取得了巨大成功,广泛应用于图像识别、物体检测、图像生成等任务。以下是 CNN 的主要理论概念: 在数学中,卷…

TeXLive 2023安装教程

TeXLive 2023安装教程 本文介绍最新TeX发行版——TeXLive 2023的安装步骤。如果你想用LaTeX进行写作&#xff0c;那么需要搭建LaTeX环境&#xff1a;可以选择下面两种方案之一进行安装&#xff1a;(1)TeXLive 2023TeXStudio或者(2)TeXLive 2023WinEdt 11。其中TeXLive 2023是由…

mqttws.js

mqttws.js 概述具体使用引入mqttws.js客户端实例化创建连接注册连接断开处理事件收到消息回调函数订阅topic取消订阅断开连接发送消息 参考文章 https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/…

[Mac软件]Downie 4.6.34视频下载工具

以下是关于Downie软件的介绍&#xff1a; Downie是一款非常实用的视频下载软件&#xff0c;专门为Mac用户设计。这款软件的使用方法非常简单&#xff0c;只需要将想要下载的视频链接复制到Downie的界面&#xff0c;它就能够自动下载。 Downie最大的特点就是支持的网站非常多&a…

centos7卸载mongodb数据库

如果您已经删除了MongoDB的相关文件&#xff0c;但系统仍然显示已经安装了一个版本&#xff0c;可能是由于之前的MongoDB安装残留导致的。您可以尝试以下步骤来彻底清除MongoDB并重新安装&#xff1a; 卸载MongoDB&#xff1a;使用以下命令卸载MongoDB&#xff1a; sudo yum er…

五大匹配算法

五大匹配算法 五大匹配算法 BF 匹配、RK 匹配、KMP 匹配、BM 匹配、Sunday 匹配。 1、BF 匹配 // BF 匹配&#xff08;暴力匹配&#xff09; public static int bfMatch(String text, String pattern) {char[] t text.toCharArray();char[] p pattern.toCharArray();int i …

vs code git问题:文件明明已加入忽略文件中,还是出现

vs code git问题&#xff1a;文件明明已加入忽略文件中&#xff0c;还是出现 原因&#xff1a; 因为之前这些文件都已经提交过&#xff0c;线上GIT已经存在&#xff0c;已存在就不能忽略&#xff0c; 解决办法&#xff1a; 先要删除这些文件提交上去&#xff0c;然后把这些文…

使用USB转JTAG芯片CH347在Vivado下调试

简介 高速USB转接芯片CH347是一款集成480Mbps高速USB接口、JTAG接口、SPI接口、I2C接口、异步UART串口、GPIO接口等多种硬件接口的转换芯片。 通过XVC协议&#xff0c;将CH347应用于Vivado下&#xff0c;简单尝试可以成功&#xff0c;源码如下&#xff0c;希望可以一起共建&a…

软件系统运维方案

1.项目情况 2.服务简述 2.1服务内容 2.2服务方式 2.3服务要求 2.4服务流程 2.5工作流程 2.6业务关系 2.7培训 3.资源提供 3.1项目组成员 3.2服务保障 点击获取所有软件开发资料&#xff1a;点我获取

一文了解Spring依赖注入时循环依赖问题

目录 什么是循环依赖 凡是Java的循环依赖都会有问题&#xff1f; 为什么Spring循环依赖会有问题&#xff1f; Spring解决循环依赖问题的思路&#xff1f; 设置二级缓存对象池 方案一直接将实例化对象放入早期对象池 方案一缺点 方案二-将实例化对象处理AOP后放入早期对象…