news 2026/5/4 10:44:48

第六部分-交互与物理——28. 射线检测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第六部分-交互与物理——28. 射线检测

28. 射线检测

1. 概述

射线检测(Raycaster)是 Three.js 中实现交互的核心技术。它通过发射射线检测与物体的交点,用于实现鼠标拾取、碰撞检测等功能。

┌─────────────────────────────────────────────────────────────┐ │ 射线检测原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 射线 (Ray) │ │ ├── 起点 (origin):射线发射位置 │ │ ├── 方向 (direction):射线方向向量 │ │ └── 距离 (distance):检测距离 │ │ │ │ 检测流程 │ │ ├── 创建射线 │ │ ├── 计算射线与物体的交点 │ │ ├── 返回交点信息 │ │ └── 处理交互 │ │ │ └─────────────────────────────────────────────────────────────┘

2. Raycaster 类

2.1 创建 Raycaster

// 创建射线检测器constraycaster=newTHREE.Raycaster();// 设置射线起点和方向constorigin=newTHREE.Vector3(0,0,0);constdirection=newTHREE.Vector3(0,0,1);raycaster.set(origin,direction);// 设置检测距离raycaster.far=100;// 近平面距离raycaster.near=0;

2.2 属性详解

属性说明
ray射线对象(origin + direction)
near近平面距离
far远平面距离
params精度参数
intersectObjects检测物体列表

3. 交点检测

3.1 检测物体列表

// 检测多个物体constintersects=raycaster.intersectObjects(objects);// 检测单个物体constintersects=raycaster.intersectObject(object);// 递归检测子物体constintersects=raycaster.intersectObjects(objects,true);

3.2 交点信息

if(intersects.length>0){consthit=intersects[0];// 被击中的物体constobject=hit.object;// 击中点的世界坐标constpoint=hit.point;// 击中点的法线constnormal=hit.normal;// 击中点的 UV 坐标constuv=hit.uv;// 从射线起点到击中点的距离constdistance=hit.distance;}

4. 鼠标交互

4.1 鼠标坐标转换

constmouse=newTHREE.Vector2();window.addEventListener('click',(event)=>{// 将鼠标坐标转换为标准化设备坐标 (-1 到 +1)mouse.x=(event.clientX/renderer.domElement.clientWidth)*2-1;mouse.y=-(event.clientY/renderer.domElement.clientHeight)*2+1;// 从相机发射射线raycaster.setFromCamera(mouse,camera);// 检测物体constintersects=raycaster.intersectObjects(objects);if(intersects.length>0){console.log('点击了物体:',intersects[0].object);}});

4.2 鼠标悬停

lethoveredObject=null;window.addEventListener('mousemove',(event)=>{mouse.x=(event.clientX/renderer.domElement.clientWidth)*2-1;mouse.y=-(event.clientY/renderer.domElement.clientHeight)*2+1;raycaster.setFromCamera(mouse,camera);constintersects=raycaster.intersectObjects(objects);if(intersects.length>0){if(hoveredObject!==intersects[0].object){if(hoveredObject){// 恢复原样式hoveredObject.material.emissive.setHex(0x000000);}hoveredObject=intersects[0].object;hoveredObject.material.emissive.setHex(0x444444);}}elseif(hoveredObject){hoveredObject.material.emissive.setHex(0x000000);hoveredObject=null;}});

5. 完整示例

import*asTHREEfrom'three';import{OrbitControls}from'three/examples/jsm/controls/OrbitControls.js';import{CSS2DRenderer,CSS2DObject}from'three/examples/jsm/renderers/CSS2DRenderer.js';constscene=newTHREE.Scene();scene.background=newTHREE.Color(0x111122);constcamera=newTHREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);camera.position.set(5,4,8);camera.lookAt(0,0,0);constrenderer=newTHREE.WebGLRenderer({antialias:true});renderer.setSize(window.innerWidth,window.innerHeight);renderer.shadowMap.enabled=true;document.body.appendChild(renderer.domElement);constlabelRenderer=newCSS2DRenderer();labelRenderer.setSize(window.innerWidth,window.innerHeight);labelRenderer.domElement.style.position='absolute';labelRenderer.domElement.style.top='0px';labelRenderer.domElement.style.left='0px';labelRenderer.domElement.style.pointerEvents='none';document.body.appendChild(labelRenderer.domElement);constcontrols=newOrbitControls(camera,renderer.domElement);controls.enableDamping=true;// 光源constambientLight=newTHREE.AmbientLight(0x404040,0.5);scene.add(ambientLight);constdirectionalLight=newTHREE.DirectionalLight(0xffffff,1);directionalLight.position.set(5,10,7);directionalLight.castShadow=true;scene.add(directionalLight);// 辅助对象constaxesHelper=newTHREE.AxesHelper(5);scene.add(axesHelper);constgridHelper=newTHREE.GridHelper(10,20);scene.add(gridHelper);// 创建可交互物体constobjects=[];constcolors=[0xff3333,0x33ff33,0x3333ff,0xffcc33,0xff33cc,0x33ccff];for(leti=0;i<6;i++){constgeometry=newTHREE.BoxGeometry(0.8,0.8,0.8);constmaterial=newTHREE.MeshStandardMaterial({color:colors[i],metalness:0.5,roughness:0.3});constcube=newTHREE.Mesh(geometry,material);constangle=(i/6)*Math.PI*2;constradius=2.5;cube.position.x=Math.cos(angle)*radius;cube.position.z=Math.sin(angle)*radius;cube.position.y=0.5;cube.castShadow=true;cube.userData={id:i,color:colors[i],originalColor:material.color.clone()};scene.add(cube);objects.push(cube);}// 地面constplaneGeometry=newTHREE.PlaneGeometry(8,8);constplaneMaterial=newTHREE.MeshStandardMaterial({color:0x336699,side:THREE.DoubleSide});constplane=newTHREE.Mesh(planeGeometry,planeMaterial);plane.rotation.x=-Math.PI/2;plane.position.y=-0.5;plane.receiveShadow=true;scene.add(plane);// 射线检测器constraycaster=newTHREE.Raycaster();constmouse=newTHREE.Vector2();// 状态lethoveredObject=null;letselectedObject=null;// 标签constcreateLabel=(text,position,color='#ffffff')=>{constdiv=document.createElement('div');div.textContent=text;div.style.color=color;div.style.background='rgba(0,0,0,0.6)';div.style.padding='2px 6px';div.style.borderRadius='4px';div.style.fontSize='12px';div.style.pointerEvents='none';constlabel=newCSS2DObject(div);label.position.copy(position);scene.add(label);returnlabel;};objects.forEach((obj,i)=>{createLabel(`物体${i+1}`,newTHREE.Vector3(obj.position.x,obj.position.y+0.8,obj.position.z));});// 信息显示constinfoDiv=document.createElement('div');infoDiv.style.position='absolute';infoDiv.style.bottom='20px';infoDiv.style.left='20px';infoDiv.style.background='rgba(0,0,0,0.7)';infoDiv.style.color='white';infoDiv.style.padding='10px';infoDiv.style.borderRadius='5px';infoDiv.style.fontFamily='monospace';infoDiv.style.fontSize='14px';document.body.appendChild(infoDiv);// 鼠标移动事件window.addEventListener('mousemove',(event)=>{mouse.x=(event.clientX/renderer.domElement.clientWidth)*2-1;mouse.y=-(event.clientY/renderer.domElement.clientHeight)*2+1;raycaster.setFromCamera(mouse,camera);constintersects=raycaster.intersectObjects(objects);if(intersects.length>0){consthit=intersects[0].object;if(hoveredObject!==hit){if(hoveredObject){hoveredObject.material.emissive.setHex(0x000000);hoveredObject.scale.set(1,1,1);}hoveredObject=hit;hoveredObject.material.emissive.setHex(0x444444);hoveredObject.scale.set(1.1,1.1,1.1);}infoDiv.innerHTML=`悬停物体:${hoveredObject.userData.id+1}<br>距离:${intersects[0].distance.toFixed(2)}`;}else{if(hoveredObject){hoveredObject.material.emissive.setHex(0x000000);hoveredObject.scale.set(1,1,1);hoveredObject=null;}infoDiv.innerHTML='无物体';}});// 点击事件window.addEventListener('click',(event)=>{if(hoveredObject){if(selectedObject===hoveredObject){selectedObject.material.color.setHex(selectedObject.userData.originalColor);selectedObject.scale.set(1,1,1);selectedObject=null;}else{if(selectedObject){selectedObject.material.color.setHex(selectedObject.userData.originalColor);selectedObject.scale.set(1,1,1);}selectedObject=hoveredObject;selectedObject.material.color.setHex(0xffaa00);selectedObject.scale.set(1.2,1.2,1.2);}}});// 动画functionanimate(){requestAnimationFrame(animate);// 旋转物体objects.forEach((obj,i)=>{obj.rotation.y+=0.01;obj.rotation.x+=0.005;});controls.update();renderer.render(scene,camera);labelRenderer.render(scene,camera);}animate();window.addEventListener('resize',onWindowResize,false);functiononWindowResize(){camera.aspect=window.innerWidth/window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth,window.innerHeight);labelRenderer.setSize(window.innerWidth,window.innerHeight);}

6. 总结

类/方法用途
Raycaster射线检测器
setFromCamera()从相机设置射线
intersectObjects()检测物体列表
intersectObject()检测单个物体
交点信息说明
object被击中的物体
point击中点坐标
normal击中点法线
distance距离
uvUV 坐标

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 10:44:12

AI对话式GTM管理:用自然语言配置Google Tag Manager标签与转化跟踪

1. 项目概述&#xff1a;用AI对话管理你的Google Tag Manager 如果你负责过网站的广告投放或数据分析&#xff0c;大概率对Google Tag Manager&#xff08;GTM&#xff09;又爱又恨。爱的是&#xff0c;它确实把我们从“求着开发改代码”的困境中解放了出来&#xff0c;让营销…

作者头像 李华
网站建设 2026/5/4 10:39:15

2000-2023年地级市数字基础设施评价指标体系

数据介绍 数字基础设施评价指标体系包括数字基础设施投入和数字基础设施产出&#xff0c;数字基础设施投入指标层包括光缆密度&#xff0c;人均互联网宽带接入端口&#xff0c;相关从业人员&#xff1b;数字基础上设施产出指标层包括电信业务收入&#xff0c;移动电话普及率&a…

作者头像 李华
网站建设 2026/5/4 10:34:43

JiYuTrainer技术解析:Windows内核级进程控制与教育软件破解方案

JiYuTrainer技术解析&#xff1a;Windows内核级进程控制与教育软件破解方案 【免费下载链接】JiYuTrainer 极域电子教室防控制软件, StudenMain.exe 破解 项目地址: https://gitcode.com/gh_mirrors/ji/JiYuTrainer JiYuTrainer是一款专注于极域电子教室系统控制的Windo…

作者头像 李华
网站建设 2026/5/4 10:28:25

新手入门:借助快马平台零代码基础构建班级宠物园下载页

今天想和大家分享一个特别适合编程新手的实践项目——用InsCode(快马)平台零基础搭建班级宠物园下载页。整个过程就像搭积木一样简单&#xff0c;完全不需要担心看不懂代码。 项目背景与目标 班级宠物园是个虚拟饲养小动物的应用&#xff0c;同学们可以一起照顾电子宠物。我们需…

作者头像 李华