如何在Qt3D场景上绘制2D图形?嵌入Qt3DWindow的实现方案咨询
在Qt3D场景上绘制2D内容的最优方案
针对你把Qt3DWindow通过QWidget::createWindowContainer嵌入Widget应用,需要叠加2D文本、图形的场景,我给你梳理三个最实用的方案,各有优劣,你可以根据需求选择:
方案1:使用Qt3D原生的Overlay Layer(推荐高性能场景)
Qt3D本身提供了Overlay Layer机制,允许你在3D场景的最上层渲染2D内容,完全融入Qt3D的渲染管线,性能最优,不会出现Widget叠加的额外开销。
实现步骤:
- 获取Qt3DWindow的
QForwardRenderer(默认渲染器),设置其overlayLayer属性。 - 创建2D元素(比如文本用
QText2DEntity,图形可以用QPlaneMesh+纹理材质),将它们添加到overlay层中。 - 调整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绘制。
实现步骤:
- 获取container widget:
QWidget* container = QWidget::createWindowContainer(qt3dWindow); - 创建一个自定义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); } }; - 配置overlay widget的属性,确保透明且不拦截鼠标事件:
OverlayWidget* overlay = new OverlayWidget(container); overlay->setGeometry(container->rect()); overlay->setAttribute(Qt::WA_TransparentForMouseEvents); // 让鼠标事件穿透到Qt3D窗口 overlay->setAttribute(Qt::WA_NoSystemBackground); // 透明背景 - 监听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元素。
实现步骤:
- 创建一个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" } } } - 在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
相关产品推荐
相关产品推荐

