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

如何在Qt3D场景上绘制2D图形?嵌入Qt3DWindow的实现方案咨询

在Qt3D场景上绘制2D内容的最优方案

针对你把Qt3DWindow通过QWidget::createWindowContainer嵌入Widget应用,需要叠加2D文本、图形的场景,我给你梳理三个最实用的方案,各有优劣,你可以根据需求选择:

方案1:使用Qt3D原生的Overlay Layer(推荐高性能场景)

Qt3D本身提供了Overlay Layer机制,允许你在3D场景的最上层渲染2D内容,完全融入Qt3D的渲染管线,性能最优,不会出现Widget叠加的额外开销。

实现步骤:

  1. 获取Qt3DWindow的QForwardRenderer(默认渲染器),设置其overlayLayer属性。
  2. 创建2D元素(比如文本用QText2DEntity,图形可以用QPlaneMesh+纹理材质),将它们添加到overlay层中。
  3. 调整2D元素的位置、大小,确保它们固定在屏幕坐标系(不会随3D相机移动)。

代码示例:

// 假设你已经有了Qt3DWindow* window和QEntity* rootEntity
auto renderer = window->activeFrameGraph()->findChild<QForwardRenderer*>();
if (renderer) {
    // 创建Overlay层
    auto overlayLayer = new QLayer(rootEntity);
    renderer->setOverlayLayer(overlayLayer);

    // 添加2D文本
    auto textEntity = new QText2DEntity(rootEntity);
    textEntity->setText("Hello Qt3D Overlay");
    textEntity->setColor(Qt::white);
    textEntity->setFont(QFont("Arial", 24));
    // 设置文本位置(屏幕坐标,原点在左下角)
    textEntity->setPosition(QVector2D(50, 50));
    // 将文本加入Overlay层
    textEntity->addComponent(overlayLayer);

    // 添加自定义图形(比如用QImage作为纹理的平面)
    auto planeMesh = new QPlaneMesh();
    planeMesh->setWidth(200);
    planeMesh->setHeight(100);
    auto texture = new QTexture2D();
    auto image = QImage(200, 100, QImage::Format_RGBA8888);
    image.fill(Qt::transparent);
    QPainter painter(&image);
    painter.setPen(Qt::red);
    painter.drawRect(10,10,180,80);
    painter.end();
    auto textureImage = new QTextureImage(texture);
    textureImage->setImage(image);
    texture->addTextureImage(textureImage);
    auto material = new QTextureMaterial();
    material->setTexture(texture);
    auto planeEntity = new QEntity(rootEntity);
    planeEntity->addComponent(planeMesh);
    planeEntity->addComponent(material);
    // 设置平面位置(屏幕坐标)
    auto transform = new QTransform();
    transform->setTranslation(QVector3D(300, 100, 0)); // z=0在overlay层最前面
    planeEntity->addComponent(transform);
    planeEntity->addComponent(overlayLayer);
}

优点:

  • 完全和Qt3D渲染同步,性能极佳,适合动态2D内容(比如实时更新的文本、动画图形)。
  • 支持Qt3D的所有材质特性,比如半透明、纹理动画等。

缺点:

  • 复杂的矢量图形需要先转成纹理,不如QPainter直接绘制方便。

方案2:在Container Widget上叠加透明QWidget(适合简单静态内容)

如果你的2D内容比较简单(比如静态文本、基础形状),可以直接在createWindowContainer返回的QWidget上面叠加一个透明的QWidget,重写它的paintEvent用QPainter绘制。

实现步骤:

  1. 获取container widget:QWidget* container = QWidget::createWindowContainer(qt3dWindow);
  2. 创建一个自定义QWidget作为container的子部件:
    class OverlayWidget : public QWidget {
    protected:
        void paintEvent(QPaintEvent*) override {
            QPainter painter(this);
            painter.setPen(Qt::yellow);
            painter.setFont(QFont("Verdana", 18));
            painter.drawText(100, 100, "Overlay Widget Text");
            painter.setBrush(QColor(255,0,0,128));
            painter.drawRect(200, 200, 150, 100);
        }
    };
    
  3. 配置overlay widget的属性,确保透明且不拦截鼠标事件:
    OverlayWidget* overlay = new OverlayWidget(container);
    overlay->setGeometry(container->rect());
    overlay->setAttribute(Qt::WA_TransparentForMouseEvents); // 让鼠标事件穿透到Qt3D窗口
    overlay->setAttribute(Qt::WA_NoSystemBackground); // 透明背景
    
  4. 监听container的resize事件,同步调整overlay的大小:
    connect(container, &QWidget::resized, overlay, [overlay](const QSize& size) {
        overlay->setGeometry(QRect(QPoint(0,0), size));
    });
    

优点:

  • 上手快,熟悉QPainter的话不用学习新API。
  • 可以直接绘制任意矢量图形、文本,无需转纹理。

缺点:

  • 属于两个独立的渲染层,动态内容较多时可能出现性能瓶颈,或者渲染不同步的问题。
  • 鼠标事件处理需要额外注意(比如穿透设置)。

方案3:结合Qt Quick + Scene3D(适合复杂2D交互/动画)

如果你的2D内容需要丰富的交互、动画效果,或者想复用Qt Quick的2D组件(比如按钮、进度条),可以把Qt3D场景嵌入到Qt Quick中,然后直接在QML里添加2D元素。

实现步骤:

  1. 创建一个QML文件(比如SceneWithOverlay.qml):
    import QtQuick 2.15
    import Qt3D.Core 2.15
    import Qt3D.Render 2.15
    import Qt3D.Extras 2.15
    
    Item {
        width: 800
        height: 600
    
        // 嵌入Qt3D场景
        Scene3D {
            anchors.fill: parent
            aspects: ["render", "input"]
            Entity {
                // 这里放你的3D内容(相机、灯光、模型等)
                Camera {
                    id: camera
                    projectionType: CameraLens.PerspectiveProjection
                    fieldOfView: 45
                    aspectRatio: width/height
                    nearPlane: 0.1
                    farPlane: 1000.0
                    position: Qt.vector3d(0, 0, 10)
                    viewCenter: Qt.vector3d(0, 0, 0)
                }
                OrbitCameraController { camera: camera }
                DirectionalLight { intensity: 0.8 }
                TorusMesh {
                    radius: 1
                    minorRadius: 0.3
                }
                PhongMaterial { diffuse: Qt.rgba(0.7, 0.7, 0.7, 1.0) }
            }
        }
    
        // 添加2D文本和图形
        Text {
            text: "Qt Quick Overlay Text"
            color: "white"
            font.pixelSize: 24
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.margins: 20
        }
    
        Rectangle {
            width: 200
            height: 50
            color: "rgba(0, 255, 0, 0.5)"
            anchors.bottom: parent.bottom
            anchors.right: parent.right
            anchors.margins: 30
            Text {
                text: "Status: Ready"
                anchors.centerIn: parent
                color: "black"
            }
        }
    }
    
  2. 在Widget应用中用QQuickWidget加载这个QML:
    QQuickWidget* quickWidget = new QQuickWidget();
    quickWidget->setSource(QUrl("qrc:/SceneWithOverlay.qml"));
    quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
    // 把quickWidget加入你的Widget布局即可
    

优点:

  • Qt Quick的2D组件库非常丰富,支持复杂的动画、交互(比如点击事件、手势)。
  • 3D和2D内容在同一个渲染管线中,性能和同步性都很好。

缺点:

  • 需要熟悉QML语法,如果之前一直用Widget开发,可能需要一定的学习成本。
  • 混合Widget和QML时,需要注意上下文和事件传递的问题。

总结一下:

  • 如果追求最高性能、和3D场景深度整合,选方案1;
  • 如果只是简单静态内容,想快速实现,选方案2;
  • 如果需要丰富的2D交互/动画,选方案3。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:44:05