拾取
引擎的 Picker API 可查询指定像素处渲染的对象,对高斯泼溅与网格的使用方式相同。
查看实时示例:体验交互选择与世界位置检测。
查看实时示例
配置拾取器
创建启用深度的 Picker 实例,将深度参数设为 true:
// Create a picker with depth buffer support for world position detection
const picker = new pc.Picker(app, 1, 1, true);
如果不仅要识别点击对象,还要取得点击位置的三维世界坐标,就需要深度缓冲区。
还必须在渲染任何单元之前,为场景 gsplat 管理器启用 ID 跟踪:
// Enable gsplat IDs so the picker can identify splat components
app.scene.gsplat.enableIds = true;
准备拾取
查询前调用 prepare() 渲染 ID 纹理:
// Get the layer containing your splats
const worldLayer = app.scene.layers.getLayerByName('World');
// Render the picking buffer
picker.prepare(camera.camera, app.scene, [worldLayer]);
可降低分辨率提升性能:
// Use quarter resolution for faster picking
const pickerScale = 0.25;
picker.resize(canvas.clientWidth * pickerScale, canvas.clientHeight * pickerScale);
// Scale the pick coordinates accordingly
const scaledX = mouseX * pickerScale;
const scaledY = mouseY * pickerScale;
识别对象
getSelectionAsync() 返回屏幕位置的拾取对象,直接返回 GSplatComponent:
const selection = await picker.getSelectionAsync(x, y, 1, 1);
if (selection.length > 0) {
// selection contains GSplatComponent instances
const gsplatComponent = selection[0];
// Find the entity that owns this component
const entity = entities.find(e => e.gsplat === gsplatComponent);
}
获取世界位置
getWorldPointAsync() 返回点击点的三维世界位置:
const worldPoint = await picker.getWorldPointAsync(x, y);
if (worldPoint) {
// worldPoint is a Vec3 containing the 3D position
console.log('Picked position:', worldPoint.x, worldPoint.y, worldPoint.z);
}
完整示例
典型流程如下:
// Enable ID tracking for splat picking
app.scene.gsplat.enableIds = true;
// Create a splat entity
const splat = new pc.Entity('splat');
splat.addComponent('gsplat', {
asset: splatAsset
});
app.root.addChild(splat);
// Create picker with depth support
const picker = new pc.Picker(app, 1, 1, true);
const handlePick = async (mouseX, mouseY) => {
// Use lower resolution for performance
const pickerScale = 0.25;
picker.resize(canvas.clientWidth * pickerScale, canvas.clientHeight * pickerScale);
// Prepare the picker
const worldLayer = app.scene.layers.getLayerByName('World');
picker.prepare(camera.camera, app.scene, [worldLayer]);
const x = mouseX * pickerScale;
const y = mouseY * pickerScale;
// Get the world position
const worldPoint = await picker.getWorldPointAsync(x, y);
if (!worldPoint) return;
// Get the picked object
const selection = await picker.getSelectionAsync(x, y, 1, 1);
if (selection.length === 0) return;
// selection contains the GSplatComponent directly
const gsplatComponent = selection[0];
// Find the entity that owns this component
const entity = splatEntities.find(e => e.gsplat === gsplatComponent);
if (entity) {
// Convert world position to entity's local space if needed
const localPos = entity.getWorldTransform()
.clone()
.invert()
.transformPoint(worldPoint);
console.log('Picked entity:', entity.name);
console.log('Local position:', localPos);
}
};
// Handle mouse clicks
app.mouse.on(pc.EVENT_MOUSEDOWN, (event) => {
handlePick(event.x, event.y);
});
// Handle touch events
app.touch.on(pc.EVENT_TOUCHSTART, (event) => {
const touch = event.touches[0];
handlePick(touch.x, touch.y);
});
调试
使用 TextureRenderer 每帧检查拾取器缓冲区。
import { TextureRenderer } from 'playcanvas';
const textures = new TextureRenderer(app);
app.on('update', () => {
if (picker.colorBuffer) {
textures.draw(picker.colorBuffer, 0.725, 0.835, 0.1, 0.1);
}
if (picker.depthBuffer) {
textures.draw(picker.depthBuffer, 0.835, 0.835, 0.1, 0.1);
}
});