跳到主要内容

拾取

引擎的 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);
}
});