场景拾取器
Picker 类提供通过单击或触摸屏幕,按屏幕坐标选择网格实例的方式。它从相机视角将场景渲染到离屏缓冲区,用唯一 ID 颜色高效识别被点击的对象。拾取器同时支持普通网格和高斯泼溅,适用于 WebGL2 和 WebGPU 后端。
基本用法
创建拾取器时,传入应用和所需分辨率,也可以启用深度拾取:
const picker = new pc.Picker(app, width, height, depth);
基本流程分三步:
- 准备:每帧调用一次
prepare(camera, scene, layers)渲染拾取缓冲区,也可仅在相机或场景变化时调用。 - 调整大小:按需调用
resize(width, height)调整拾取器分辨率。 - 查询:使用
getSelectionAsync(x, y, width, height)异步获取拾取的网格实例。
拾取器通过异步 API 读取像素数据,不阻塞渲染线程,从而在拾取时也能保持平稳帧率。用完后调用 destroy() 释放 GPU 资源。
完整 API 文档参见 Picker API 参考。
深度支持
默认情况下,拾取器仅捕获网格实例 ID。可将构造函数第四个参数设为 true 来启用深度拾取:
const picker = new pc.Picker(app, width, height, true);
启用后,拾取器会在网格 ID 之外同时捕获深度值。这些信息可用于计算对象表面点击位置的精确 3D 世界坐标,适合放置对象、测量距离或创建编辑器工具。
世界位置拾取
启用深度拾取后,可使用 getWorldPointAsync(x, y) 获取屏幕坐标处的 3D 世界位置:
picker.getWorldPointAsync(x, y).then((worldPoint) => {
if (worldPoint) {
// worldPoint is a Vec3 in world space
console.log('Clicked at:', worldPoint);
} else {
// No object was clicked (background)
console.log('Clicked on empty space');
}
});
该方法返回一个 Promise,解析结果是包含世界位置的 Vec3;如果未点击任何对象,则为 null。它同时适用于透视和正交相机。
性能注意事项
可以通过以下方式优化拾取器性能:
降低分辨率:以屏幕分辨率的一部分渲染拾取缓冲区,可以显著提升性能。例如使用屏幕分辨率的 0.25 倍:
const pickerScale = 0.25;
const picker = new pc.Picker(
app,
canvas.width * pickerScale,
canvas.height * pickerScale,
true
);
代价是精度降低:较低分辨率可能漏掉很小的对象。
异步读取:异步 API 从 GPU 读取像素数据时不会阻塞主线程,有助于保持平稳帧率。
选择性更新:仅在需要时调用 prepare()。如果相机和对象均静止,可复用先前渲染的拾取缓冲区,无需再次调用 prepare()。
高斯泼溅支持
拾取器全面支持高斯泼溅实例,使用与普通网格相同的 API。可以通过网格实例 ID 拾取高斯泼溅实例;启用深度后,还可以确定其表面的精确 3D 位置。
这可用于在高斯泼溅上放置标记、测量距离,或在复杂场景中选择单个高斯泼溅实体等交互应用。完整演示参见高斯泼溅拾取示例。
示例
以下引擎示例演示拾取器的使用:
- 区域拾取器:演示如何在矩形屏幕区域中拾取网格实例,并提供视觉反馈。
- 高斯泼溅拾取:演示拾取高斯泼溅实例,并通过世界位置拾取在其表面放置标记。