射线拾取
射线(Ray)是一条自起点而始,朝着指定方向无限延伸的射线,而Raycaster
是 3D 场景中的射线发射器,可以将射线实体化,提供更丰富的射线初始化方法,并提供射线与场景内其它网格模型的相交检测能力。
示例
使用
创建
// 初始化Raycaster实例,接受射线起点、方向的入参
const raycaster = new Raycaster(start?: Vector3, direction?: Vector3);
创建Raycaster
实例后,有三种方法生成射线。
直接生成射线
.set(start: Vector3, direction: Vector3)
方法可以直接改变射线发射器中射线的起点及方向信息。
const start = new Vector3(0, 0, 0); // 以 (0,0,0) 为射线起点
const direction = new Vector3(1, 0, 0); // 以 (1,0,0) 为射线方向
// 直接设置
raycaster.set(start, direction);
由相机中心生成射线
.setFromCamera( camera: Camera)
方法可以创建从camera
中心发出的射向屏幕中心的射线。
// 点击画布
canvas.onclick = e => {
e.preventDefault();
// 获取鼠标点击坐标
const { clientX, clientY } = e;
raycaster.setFromCamera(camera);
};
由视口坐标生成射线
.setFromViewportPoint(camera: Camera, coords: IScreenCoordinate, canvasSize: IScreenCoordinate)
方法创建以相机中心为起点,到视口坐标所指向的世界坐标的射线。
canvas.onclick = e => {
e.preventDefault();
// 获取鼠标点击坐标
const { clientX, clientY } = e;
raycaster.setFromViewportPoint(
// 相机
camera,
// 视口坐标
{
x: clientX,
y: clientY,
},
// 画布宽高
{
x: canvasWidth,
y: canvasHeight,
},
);
};
相交检测
Raycaster
有 4 种相交检测方法。
获取与平面的相交点
.intersectPlaneAt(p:Plane)
可以检测当前射线是否与某平面的相交点,若不相交则返回null
。
const plane = new Plane();
raycaster.intersectPlaneAt(plane); // 返回Vector3类型的相交点,或者null
获取与包围盒的相交点
.intersectBBoxAt(box:BBox)
可以检测当前射线是否与某包围盒的相交点,若不相交则返回null
。
raycaster.intersectPlaneAt(mesh.bbox); // 返回与某网格的包围盒的相交点,或者null
获取最近的相交模型
.getNearestHitResult(root: Object3D)
将返回射线与root
下所有网格模型中相交的、距离相机最近的网格模型,若均不相交则返回undefined
。
raycaster.getNearestHitResult(scene); // 返回与场景中相交的最近的网格模型
获取所有相交模型
.getAllHitResults(root: Object3D)
将返回射线与root
下所有网格模型中发生相交的所有网格模型。
raycaster.getAllHitResults(scene); // 返回与场景中相交的所有网格模型
API
RayCasterQ&A
- 为什么某网格模型明明不应该与射线相交,却被判定相交?
Tinoe
中网格模型的包围盒为AABB
类型,本身不够精确。- 而射线相交检测的依据恰恰是网格模型的包围盒,因此可能会出现误检情况。想要实现精准拾取,建议使用 GPU 拾取。