介绍
顾名思义,Raycaster 可以向特定方向投射(或发射)一条射线,并测试与它相交的对象。
您可以使用该技术来检测玩家前面是否有墙,测试激光枪是否击中了什么东西,测试当前鼠标下方是否有东西来模拟鼠标事件,以及许多其他事情。
设置
在我们的启动器中,我们有 3 个红色球体,我们将射出一条光线,看看这些球体是否相交。
创建光线投射器
实例化一个Raycaster:
/**
* Raycaster
*/
const raycaster = new THREE.Raycaster()
要改变光线投射的位置和方向,我们可以使用 set(...)
方法。第一个参数是position
,第二个参数是direction
。它需要两个向量作为参数:一个是起点位置,另一个是方向。
两者都是Vector3,但direction
必须进行归一化。归一化向量的长度为1
. 别担心,你不必自己做数学运算,你可以调用normalize()
向量上的方法:
const rayOrigin = new THREE.Vector3(- 3, 0, 0)
const rayDirection = new THREE.Vector3(10, 0, 0)
rayDirection.normalize()
raycaster.set(rayOrigin, rayDirection)
在这个例子中,起点位置是(-3, 0, 0),方向是(10, 0, 0),我们通过normalize()方法将方向向量归一化处理,使其长度为1,表示这是一个单位向量。
然后将起点位置和方向向量设置为射线的起点和方向,最后使用raycaster.set()方法将它们传递给Raycaster对象。这个射线可以用于碰撞检测或者其他的渲染相关工作。
在这里,光线位置应该从我们场景的左侧开始,方向似乎向右。我们的光线应该穿过所有球体。
投射光线
要投射光线并获得相交的对象,我们可以使用两种方法,intersectObject(...)
(单数)和intersectObjects(...)
(复数)。
intersectObject(...)
将测试一个对象并将intersectObjects(...)
测试一组对象:
const intersect = raycaster.intersectObject(object2)
console.log(intersect)
const intersects = raycaster.intersectObjects([object1, object2, object3])
console.log(intersects)
如果您查看日志,您会看到intersectObject(...)
返回了一个包含一个对象的数组(可能是第二个球体)并且 intersectObjects(...)
返回了一个包含三个对象的数组(可能是 3 个球体的集合)。
交集的结果
raycaster.intersectObject
交集的结果始终是一个数组,即使您只测试了一个对象。那是因为一条光线可以多次穿过同一个物体。想象一个甜甜圈。光线将穿过环的第一部分,然后穿过中间的孔,然后再次穿过环的第二部分,这样交集的结果就是2个对象了。
返回数组的每一项都包含很多有用的信息:
distance
:射线原点和碰撞点之间的距离。face
:几何体的哪个面被光线击中。faceIndex
: 那张脸的索引。object
: 碰撞涉及什么对象。point
:碰撞在 3D 空间中的确切位置的Vector3 。uv
:该几何体中的 UV 坐标。
使用哪个数据取决于您。如果你想测试玩家面前是否有墙,你可以测试distance
的值. 如果要更改对象的颜色,可以更新 object的材质。如果你想在冲击点上显示爆炸特效,你可以在该point
位置创建这个爆炸动画。
测试每一帧
目前,我们一开始只投射一条光线。如果我们想在物体移动时对其进行测试,我们必须在每一帧上进行测试。让我们为球体设置动画,并在光线与它们相交时将它们变成蓝色。
删除我们之前所做的代码,只保留 raycaster
实例化:
const raycaster = new THREE.Raycaster()
通过使用tick
函数中的经过时间和经典Math.sin(...)
来为球体制作动画:
const clock = new THREE.Clock()
const tick = () =>
{
const elapsedTime = clock.getElapsedTime()
// Animate objects
object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
// ...
}
您应该看到球体以不同的频率上下波动。
现在让我们在tick
函数中 像以前一样更新我们的 raycaster
:
const clock = new THREE.Clock()
const tick = () =>
{
// ...
// Cast a ray
const rayOrigin = new THREE.Vector3(- 3, 0, 0)