浏览器中浏览3D图形的时候,想要与3D图形之间做一些点击事件和交互操作,比较常用的一个解决方案就是使用Raycaster
对象来实现(射线拾取)。
光线投射Raycaster API简述
光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
Raycaster( origin : Vector3, direction : Vector3, near : Float, far : Float )
- origin:光线投射的原点向量。
- direction:向射线提供方向的方向向量,应当被标准化。
- near:返回的所有结果比near远。near不能为负值,其默认值为0。
- far:返回的所有结果都比far近。far不能小于near,其默认值为Infinity(正无穷。)
属性
- .far:远距离因数(投射远点)。这个值表明哪些对象可以基于该距离而被
raycaster
所丢弃。 这个值不应当为负,并且应当比near
属性大。 - .near:近距离因数(投射近点)。这个值表明哪些对象可以基于该距离而被
raycaster
所丢弃。 这个值不应当为负,并且应当比far
属性小。 - .camera : 对依赖于视图的对象进行光线投射时使用的相机。默认为空。
- .layers:Raycaster 在执行相交测试时使用它来选择性地忽略 3D 对象。
- .ray:用于进行光线投射的射线。
方法
- .set ( origin : Vector3, direction : Vector3 ) : undefined:设置射线的起点和方向。
origin
一个Vector3对象,光线投射的原点向量(射线起点)。direction
一个Vector3对象,为光线提供方向的标准化方向向量(射线方向)。
- .setFromCamera ( coords : Vector2, camera : Camera ) : undefined:通过相机设置射线。
coords
在标准化设备坐标中鼠标的二维坐标,X分量与Y分量应当在-1到1之间。camera
射线所来源的摄像机。
- .intersectObject ( object : Object3D, recursive : Boolean, optionalTarget : Array ) : Array:检测所有在射线与物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个。
object
检查与射线相交的物体。recursive
若为true,则同时也会检查所有的后代。否则将只会检查对象本身。默认值为true。optionalTarget
(可选)设置结果的目标数组。如果不设置这个值,则一个新的Array会被实例化;如果设置了这个值,则在每次调用之前必须清空这个数组(例如:array.length = 0;)。
- .intersectObjects ( objects : Array, recursive : Boolean, optionalTarget : Array ) : Array:检测所有在射线与物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个
objects
检测和射线相交的一组物体。recursive
若为true,则同时也会检测所有物体的后代。否则将只会检测对象本身的相交部分。默认值为true。optionalTarget
(可选)设置结果的目标数组。如果不设置这个值,则一个新的Array会被实例化;如果设置了这个值,则在每次调用之前必须清空这个数组(例如:array.length = 0;)。
注意点
intersectObject
和intersectObjects
方法返回一个包含有交叉部分的数组,凡是选中的都会以数组的形式返回,返回数据结构是:[ { distance, point, face, faceIndex, object }, ... ]
distance
射线投射原点和相交部分之间的距离。point
射线与物体相交的第一个顶点(世界坐标)face
与射线相交的面faceIndex
与射线相交的平面的索引object
相交的物体,一般是Mesh,point,Line等。
- 如果两个网格模型屏幕坐标位置是重合的,那么都会被选中,因此可以通过数组下标的形式访问第几个对象, 被选中的网格模型对象以
object
属性的形式存在,代码intersects[0].object
就表示被选中所有的网格模型中的第一个网格模型对象。 例如通过语句intersects[0].object.material.opacity = 0.6;
可以更改材质对象的透明度。 - 当计算这条射线是否和物体相交的时候,Raycaster将传入的对象委托给
raycast
方法。这将可以让mesh
对于光线投射的响应不同于lines
和pointclouds
。 - 对于网格来说,面必须朝向射线的原点,以便其能够被检测到。 用于交互的射线穿过面的背侧时,将不会被检测到。如果需要对物体中面的两侧进行光线投射,你需要将
material
中的side
属性设置为THREE.DoubleSide
。
射线拾取使用
在 three.js 中利用射线Raycaster
进行碰撞检测获取射线穿透对象。射线最主要的用途就是拾取物体,简单说,我们在屏幕上添加一个点击事件,然后以屏幕的位置发射一条射线,执行拾取所有场景的物体,并拿到拾取到的物体,做对应的交互事件,这样我们一套点击事件就完成了。主要步骤:
- 绑定点击事件。
- 获取点击时的位置并创建标准化设备坐标。
- 根据点击时的标准化设备坐标创建射线.
- 根据逻辑,让整个数组的物体发生交互事件,或者指定拾取到的第一个,让其发生交互事件。
创建射线
// 创建射线对象
let rayCaster = new THREE.Raycaster();
// 创建一个用来存储标准化设备坐标的二维向量
let mouse = new THREE.Vector2();
绑定点击事件
window.addEventListener( 'click', event => {
// 获取鼠标位置
let x = event.clientX;
let y = event