Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解

本文主要是介绍Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

Web开发中,Three.js是一个极为强大的库,它让开发者能够轻松地在浏览器中创建和展示3D图形。随着3D技术在网页设计、游戏开发、数据可视化等领域的广泛应用,用户与3D场景的交互变得日益重要。而要实现这种交互,一个核心的技术就是光线投射(Raycasting)。通过Three.js提供的Raycaster类,我们可以检测鼠标或触摸事件在3D空间中的对应位置,进而实现点击、悬停等交互效果。本文将深入探讨如何使用Three.jsRaycaster来实现3D场景的交互事件。
光线投射原理及其属性介绍

通过Raycaster拾取模型进行轮廓高亮走这里>>>

什么是Raycasting?

Raycasting是一种计算机图形学技术,用于确定从一个点(通常是观察者的位置或屏幕上的某一点)发射出的光线与场景中物体的交点。在3D应用中,这一技术常用于模拟光照效果、碰撞检测以及用户交互。简单来说,当你在屏幕上点击或触摸时,Three.js会从该点向场景发射一条虚拟的射线,然后检查这条射线与场景中哪些对象相交,从而判断用户点击了哪个对象。
Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解
这个类用于进行raycasting(光线投射)。 光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。

Three.js中的Raycaster

Three.js中,Raycaster类是实现这一功能的关键。它允许你创建一个射线,并提供方法来检测这个射线与场景中对象的交点。以下是使用Raycaster的基本步骤:

Raycaster实例解释

new THREE.Raycaster(origin, direction, near, far)

参数说明:

origin - 光线投射的原点,Vector3类型。
direction - 射线的方向,Vector3类型。
near - 投射近点,不能为负值,应该小于far,其默认值为0
far 投射远点,不能小于near,其默认值为无穷大。

射线交叉对象

创建的光线投射对象有一个intersectObject()方法用来获取射线交叉的对象,使用方法如下

const raycaster = new THREE.Raycaster(origin, direction, near, far)
const arr= raycaster.intersectObjects(object, recursive,optionalTarget)

raycaster.intersectObjects()参数

object - 要检查的是否与射线相交的对象,Object3D类型。
recursive - 是否检查所有后代,可选默认为falseBoolean类型。
optionalTarget - 可选参数,放置结果的目标数组。Array类型。若使用这个参数返回检查结果则在每次调用之前必须清空这个数组。

raycaster.intersectObjects()的返回值

Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解

distance - 射线投射原点和相交部分之间的距离。
point - 相交部分的坐标。
face - 相交的面。
faceIndex - 相交的面的索引。
object - 相交的物体。
uv - 相交部分的点的UV坐标。

光线投射示例

Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解

示例步骤

步骤一、创建射线

const raycaster = new THREE.Raycaster()

步骤二、用一个二维向量保存鼠标点击画布上的位置

const mouse = new THREE.Vector2(1, 1)

步骤三、监听窗口事件,将x,y轴归一化坐标,通过摄像机和鼠标的位置更新色线,计算物体和射线的焦点能不能碰到物体,碰到物体后随机改变射线照射物体的颜色。

window.addEventListener("click",(e)=>{//设置鼠标向量的x,y值,将XY轴归一化,X从-1到1,Y为从-1到1,所以除以2mouse.x = (e.clientX/window.innerWidth)*2-1mouse.y = -(e.clientY/window.innerHeight)*2+1// 通过摄像机和鼠标的位置,更新射线raycaster.setFromCamera(mouse,this.camera)//计算物体和射线的焦点能不能碰到物体const intersects = raycaster.intersectObjects([sphere1,sphere2,sphere3])if(intersects.length>0){intersects[0].object.material.color.set(this.color16())}
})
代码解释:
raycaster.setFromCamera(mouse,this.camera)

每次渲染循环中,你需要更新射线的起点(通常是相机的位置)和方向(通常是基于鼠标坐标计算出的向量):这里,mouse是归一化设备坐标(即范围在(-1, -1)(1, 1)之间的坐标),可以通过监听鼠标或触摸事件并使用THREE.Vector2和renderer.domElement.clientWidth/Height进行转换得到。

const intersects = raycaster.intersectObjects(scene.children, true);

使用raycaster.intersectObjects()方法来检测射线与场景中对象的交点:此方法返回一个数组,包含了所有与射线相交的对象信息。如果数组不为空,说明有对象被选中,你可以根据需要处理这些交点信息。

什么是归一化坐标:归一化坐标,是一个二维坐标,仅有X/Y两个维度,且X和Y的取值范围均为[-1, 1],坐标原点位于three.js所创建的canvas的中心处。 ​
Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解
归一化坐标公式:

mouse.x = ((event.clientX - container.getBoundingClientRect().left) / container.getBoundingClientRect().width) * 2 - 1;
mouse.y = - ((event.clientY - container.getBoundingClientRect().top) / container.getBoundingClientRect().height) * 2 + 1;

以上示例完整代码:

<template><div id="container"></div>
</template><script>
import * as THREE from 'three'
// webGL兼容
import WebGL from 'three/examples/jsm/capabilities/WebGL.js';
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
// 轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"
//导入RGBRload加载器
import { RGBELoader } from  "three/examples/jsm/loaders/RGBELoader.js"
//导入场景模型加载器
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js"
//导入模型解压器
import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader.js"
export default {name: 'HomeView',components: {},mounted(){this.init()},data(){return {camera: null,  //相机对象scene: null,  //场景对象renderer: null,  //渲染器对象mesh: null,  //网格模型对象Meshmesh2:null,controls:null, //轨道控制器material2:null, //父元素planeMesh:null, //平面rgbeLoacer:null,}},methods:{//随机生成十六进制颜色color16(){//十六进制颜色随机var r = Math.floor(Math.random()*256);var g = Math.floor(Math.random()*256);var b = Math.floor(Math.random()*256);var color = '#'+r.toString(16)+g.toString(16)+b.toString(16);return color;},init(){let container = document.body;//创建一个场景this.scene = new THREE.Scene()//透视摄像机this.camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,700)//创建渲染器this.renderer = new THREE.WebGLRenderer();//渲染器尺寸this.renderer.setSize( window.innerWidth,  window.innerHeight );    // 创建三个球const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(1,32,32),new THREE.MeshBasicMaterial({color:0x00ff00}))sphere1.position.x = -3this.scene.add(sphere1)const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(1,32,32),new THREE.MeshBasicMaterial({color:0xff0000}))sphere2.position.x = 0this.scene.add(sphere2)const sphere3 = new THREE.Mesh(new THREE.SphereGeometry(1,32,32),new THREE.MeshBasicMaterial({color:0x0000ff}))      sphere3.position.x = 3this.scene.add(sphere3)//创建射线const raycaster = new THREE.Raycaster()//用一个二维向量保存鼠标点击画布上的位置const mouse = new THREE.Vector2(1, 1)   window.addEventListener("click",(e)=>{//设置鼠标向量的x,y值,将XY轴归一化,X从-1到1,Y为从-1到1,所以除以2mouse.x = (e.clientX/window.innerWidth)*2-1mouse.y = -(e.clientY/window.innerHeight)*2+1console.log(mouse.x,mouse.y)// 通过摄像机和鼠标的位置,更新涉嫌raycaster.setFromCamera(mouse,this.camera)//计算物体和射线的焦点能不能碰到物体const intersects = raycaster.intersectObjects([sphere1,sphere2,sphere3])console.log("intersects",intersects)if(intersects.length>0){intersects[0].object.material.color.set(this.color16())}})this.scene.background=new THREE.Color(0x999999)// 设置相机位置this.camera.position.z = 15;   this.camera.position.y =2;  this.camera.position.x = 2; // 看的方向 this.camera.lookAt(0,0,0)//添加世界坐标辅助器const axesHelper = new THREE.AxesHelper(3) this.scene.add( axesHelper );//添加轨道控制器this.controls = new OrbitControls(this.camera,this.renderer.domElement)//添加阻尼带有惯性this.controls.enableDamping = true//设置阻尼系数this.controls.dampingFactor = 0.05//元素中插入canvas对象container.appendChild(this.renderer.domElement); if ( WebGL.isWebGLAvailable() ) {this.animate();} else {const warning = WebGL.getWebGLErrorMessage();document.getElementById( document.body ).appendChild( warning );}},//旋转起来animate() {this.controls.update()requestAnimationFrame( this.animate );// this.mesh.rotation.x += 0.01;// this.mesh.rotation.y += 0.01;this.renderer.render( this.scene, this.camera );}}
}
</script>

总结

通过Three.jsRaycaster,我们能够以直观且高效的方式实现3D场景中的交互事件。无论是简单的点击反馈,还是复杂的拖拽操作,Raycasting都是构建互动式3D体验不可或缺的一部分。掌握这项技术,无疑能极大提升你的3D应用或游戏的用户体验。希望本文能为你开启探索Three.js交互世界的大门,让你的3D项目更加生动有趣。

这篇关于Three.js中的Raycasting技术:实现3D场景交互事件的Raycaster详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1035409

相关文章

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.