涉及2D内容的Qt Quick 3D场景
3D世界中的2D元素
Qt Quick 3D提供高效创建和渲染结合3D和2D元素的场景。
我们所说的结合3D-2D场景是指什么?
根据其本质,一个代表2D场景中3D视口的View3D对象可以很容易地与Qt Quick项结合,例如,矩形、图片、文本等,围绕、在下面或在上面放置在View3D项周围,而View3D项本身也是一个Qt Quick Item。
考虑以下示例
import QtQuick import QtQuick3D Rectangle { gradient: Gradient { GradientStop { position: 0; color: "steelblue" } GradientStop { position: 1; color: "black" } } Text { text: "Hello 2D World" font.pointSize: 32 color: "red" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter } Item { width: 400; height: 400 anchors.centerIn: parent View3D { anchors.fill: parent environment: SceneEnvironment { backgroundMode: SceneEnvironment.Color; clearColor: "lightGray" } PerspectiveCamera { z: 600 } DirectionalLight { } Model { source: "#Cube" materials: PrincipledMaterial { baseColor: "green"; metalness: 0.0; roughness: 0.0 } eulerRotation: Qt.vector3d(30, 45, 0) } } } }
在这里,3D场景是灰色背景的区域。窗口的其余部分由2D Qt Quick项组成。这些项可以与View3D重叠,但不属于3D世界,不使用3D坐标系,也不参与3D场景的变换。
如果我们想在3D世界中添加2D项并让它们真正地参与所有3D变换,该怎么办?例如,我们能否在3D世界中放置Rectangle和Text项,使它们跟随立方体的旋转,并且始终放置在其上方?
在接下来的部分中,我们将探讨如何实现这一点。虽然示例使用Rectangle和Text,但任何Qt Quick内容,包括Qt Quick控件、Shape、ShaderEffect、ParticleSystem都可以如此使用。
注意:也存在其他方法可用来整合2D内容与3D对象。向3D节点添加2D项允许在3D世界中自由组合2D和3D对象,但不会允许在3D对象表面上渲染2D内容。如果目标是用Qt Quick生成的内容纹理化3D网格,请使用Texture的sourceItem属性。
用作纹理图的Qt Quick内容 | 3D场景中的Qt Quick项 |
---|---|
将2D项添加到3D节点
关键因素是Object3D接受Item子对象并按特殊方式处理的能力。Object3D是Node类型的基类。这意味着任何Node以及Model等类型都可以接受Item子对象。
从Qt 6.0开始,将二维项添加到三维节点不再触发将二维内容渲染为OpenGL纹理、Vulkan图像或类似内容。相反,默认模式是将二维项与三维场景中的其他内容一起行内渲染,在同一个渲染通道中。二维项应用了所有的三维转换。这些转换从包装的节点继承。
import QtQuick import QtQuick3D Rectangle { gradient: Gradient { GradientStop { position: 0; color: "steelblue" } GradientStop { position: 1; color: "black" } } Text { text: "Hello 2D World" font.pointSize: 32 color: "red" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter } Item { width: 400; height: 400 anchors.centerIn: parent View3D { anchors.fill: parent environment: SceneEnvironment { backgroundMode: SceneEnvironment.Color; clearColor: "lightGray" } PerspectiveCamera { z: 600 } DirectionalLight { } Model { Node { y: 150 Rectangle { anchors.horizontalCenter: parent.horizontalCenter color: "white" width: text3d.width height: text3d.height Text { id: text3d text: "Hello 3D World" font.pointSize: 32 } } } source: "#Cube" materials: PrincipledMaterial { baseColor: "green"; metalness: 0.0; roughness: 0.0 } eulerRotation: Qt.vector3d(30, 45, 0) } } } }
与第一个代码段相比,模型节点现在有一个子节点,该节点通过转换将其放置在立方体位置的上方。`150` 是相对立方体中心的值,在三维坐标空间中。
Model { Node { y: 150
然后是 矩形 项。当将其添加到 节点 下方时,三维和二维世界的边界会在内部交叉,但这对于应用程序设计者来说是透明的。会自动生成一个不可见的 内容项
,允许矩形引用 父节点
并执行锚定。节点的三维转换应用于整个二维子树。在示例中,这意味着旋转将与立方体的旋转相匹配。
Node { y: 150 Rectangle { anchors.horizontalCenter: parent.horizontalCenter
二维和三维中的坐标系
二维项继续使用Qt Quick的坐标系:Y轴从上到下运行,单位对应于像素。另一方面,三维节点使用三维坐标系:Y轴向上指,单位对应于厘米,并受相机的透视投影影响。
默认情况下,顶级项的左上角放置在节点的原点处。这意味着二维子树中的顶级项通常会希望指定一个锚点,例如 anchors.centerIn: parent
,或者,如在示例中一样,将水平中心锚定到父节点的水平中心,从而在三维节点上水平居中二维内容。
进一步考虑事项
- 虽然二维项目会与三维对象一起行内渲染,但它们不参与光照,也不会投射阴影。
- 裁剪 可能不会像预期那样表现,应避免使用。如果裁剪对二维项目的布局至关重要,应用程序应明确回退到将内容渲染到纹理。这可以通过向3D节点下的顶级 `
Item
` 添加 `layer.enabled: true
` 来实现。 - 从Qt 6.2开始,按需将输入传递给二维项。来自指定位器的输入必须发生在线性声明的项的 `
childrenRect
` 内。 - 虽然将二维项树添加到三维场景相对便宜,但应该避免在三维场景内放置过多的二维子树(数百个或更多),因为这可能会增加内存和图形资源的使用。请注意,这指的是在三维节点下的分离项子树的数量,而不是那个子树中的二维项目总数。例如,上面的QML片段中只有一个二维子树。
© 2024 Qt公司有限責任公司。本文件所述文档贡献是各自拥有者的版权。本文件的文档根据自由软件基金会发布的 GNU自由文档许可版1.3 许可条款提供。Qt及其相关徽标是芬兰和/或其他国家的世界各地的Qt公司有限責任公司的商标。所有其他商标均为各自拥有者的财产。