跳到主要内容

射线拾取

射线(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

RayCaster

Q&A

  • 为什么某网格模型明明不应该与射线相交,却被判定相交?
    • Tinoe中网格模型的包围盒为AABB类型,本身不够精确。
    • 而射线相交检测的依据恰恰是网格模型的包围盒,因此可能会出现误检情况。想要实现精准拾取,建议使用 GPU 拾取