当前位置:   article > 正文

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

three.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)
  • 1

参数说明:

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)
  • 1
  • 2

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()
  • 1

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

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

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

window.addEventListener("click",(e)=>{
  //设置鼠标向量的x,y值,将XY轴归一化,X从-1到1,Y为从-1到1,所以除以2
  mouse.x = (e.clientX/window.innerWidth)*2-1
  mouse.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())
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
代码解释:
raycaster.setFromCamera(mouse,this.camera)
  • 1

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

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

使用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;
  • 1
  • 2

以上示例完整代码:

<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,  //网格模型对象Mesh
      mesh2: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 = -3
      this.scene.add(sphere1)
      const sphere2 = new THREE.Mesh(
        new THREE.SphereGeometry(1,32,32),
        new THREE.MeshBasicMaterial({
          color:0xff0000
        })
      )
      sphere2.position.x = 0
      this.scene.add(sphere2)
      const sphere3 = new THREE.Mesh(
        new THREE.SphereGeometry(1,32,32),
        new THREE.MeshBasicMaterial({
          color:0x0000ff
        })
      )      
      sphere3.position.x = 3
      this.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,所以除以2
        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,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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/784077
推荐阅读
相关标签
  

闽ICP备14008679号