You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt3D中如何设置部分Entity不可拾取(已绑定RootEntity的QObjectPicker)

解决Qt3D中部分Entity不可拾取的问题

看起来你在Qt3D的拾取控制上踩了个常见的坑——混淆了Entity树的事件传递和FrameGraph的拾取渲染控制。我来帮你梳理清楚问题,并给出两种可行的解决方法。

问题根源分析

你在RootEntity(或父Entity)上添加了QObjectPicker,默认情况下,点击任何子Entity都会触发父Picker的事件(事件冒泡机制)。而你尝试用QNoPicking和QLayerFilter的方式没生效,是因为FrameGraph的配置逻辑错误:你给不可拾取层添加了QNoPicking分支,这相当于对这些Entity做了一次无拾取的重复渲染,而非阻止它们被拾取系统检测。


方法一:在拾取事件中直接过滤(简单快捷)

这种方法不需要修改复杂的FrameGraph,只需要在Picker的点击事件里判断目标Entity是否属于不可拾取层,直接忽略即可。

步骤:

  1. 给所有不可拾取的Entity添加noPickingLayer组件;
  2. 在clicked信号的槽函数中,检查点击的Entity是否包含该层,若包含则跳过处理。

修正后的代码片段:

在loadScene函数的连接部分修改:

connect(picker, &Qt3DRender::QObjectPicker::clicked, this, [this, noPickingLayer](Qt3DRender::QPickEvent* event) {
    // 检查点击的Entity是否属于不可拾取层
    if (event->entity()->componentsOfType<Qt3DRender::QLayer>().contains(noPickingLayer)) {
        return; // 忽略不可拾取的Entity
    }
    qDebug() << "点击了可拾取的Entity:" << event->entity();
});

方法二:通过FrameGraph控制拾取范围(符合Qt3D设计,性能更优)

这种方法从根源上阻止不可拾取的Entity参与拾取渲染,避免生成不必要的拾取事件,性能更好。

步骤:

  1. 创建两个Layer:pickableLayer(可拾取)和nonPickableLayer(不可拾取);
  2. 给需要拾取的Entity添加pickableLayer,不需要的添加nonPickableLayer;
  3. 修改FrameGraph,添加一个仅包含pickableLayer的QLayerFilter,让拾取系统只处理这些Entity。

修正后的完整代码关键部分:

构造函数中的FrameGraph修改:

// 创建两个控制层
Qt3DRender::QLayer* pickableLayer = new Qt3DRender::QLayer();
Qt3DRender::QLayer* nonPickableLayer = new Qt3DRender::QLayer();

// ... 其他相机、控制器、渲染状态代码保持不变 ...

// 添加拾取控制的LayerFilter:仅让可拾取层参与拾取
Qt3DRender::QLayerFilter* pickableFilter = new Qt3DRender::QLayerFilter(cameraSelector);
pickableFilter->addLayer(pickableLayer);

viewer3D->setActiveFrameGraph(surfaceSelector);

// 传递两个层到场景加载函数
loadScene(rootEntity, pickableLayer, nonPickableLayer);

loadScene函数修改:

void QtPicking::loadScene(Qt3DCore::QEntity* rootEntity, Qt3DRender::QLayer* pickableLayer, Qt3DRender::QLayer* nonPickableLayer)
{
    Qt3DExtras::QPhongMaterial* material = new Qt3DExtras::QPhongMaterial(rootEntity);
    material->setAmbient(Qt::black);

    Qt3DCore::QEntity* parentEntity = new Qt3DCore::QEntity(rootEntity);
    // 如果父Entity本身需要可拾取,记得也添加pickableLayer
    // parentEntity->addComponent(pickableLayer);

    // 可拾取的子Entity
    Qt3DCore::QEntity* child1 = new Qt3DCore::QEntity(parentEntity);
    Qt3DExtras::QSphereMesh* child1Mesh = new Qt3DExtras::QSphereMesh;
    child1Mesh->setRadius(1);
    Qt3DCore::QTransform* child1Transform = new Qt3DCore::QTransform();
    child1Transform->setTranslation(QVector3D(2, 2, 2));
    child1->addComponent(child1Mesh);
    child1->addComponent(child1Transform);
    child1->addComponent(material);
    child1->addComponent(pickableLayer); // 标记为可拾取

    // 不可拾取的子Entity
    Qt3DCore::QEntity* child2 = new Qt3DCore::QEntity(parentEntity);
    Qt3DExtras::QSphereMesh* child2Mesh = new Qt3DExtras::QSphereMesh;
    child2Mesh->setRadius(2);
    child2->addComponent(child2Mesh);
    child2->addComponent(material);
    child2->addComponent(nonPickableLayer); // 标记为不可拾取

    Qt3DRender::QObjectPicker* picker = new Qt3DRender::QObjectPicker();
    picker->setEnabled(true);
    parentEntity->addComponent(picker);

    connect(picker, &Qt3DRender::QObjectPicker::clicked, this, [this](Qt3DRender::QPickEvent* event) {
        qDebug() << "点击了可拾取的Entity:" << event->entity();
    });
}

关键注意点

  • 如果父Entity本身需要被拾取,记得给它也添加pickableLayer;
  • QNoPicking是FrameGraph的渲染状态节点,用来禁用某个分支的拾取,而非直接给Entity添加的组件;
  • 方法二更适合大规模场景,能减少不必要的拾取计算;方法一则适合快速迭代小场景。

内容的提问来源于stack exchange,提问作者Romain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 20:02:29