将Qt 3D 移植到Qt Quick 3D :迁移指南
本指南适用于目前使用 Qt 3D的开发人员迁移到 Qt Quick 3D.
范围差异
虽然 Qt 3D和 Qt Quick 3D看似相似,都提供 3D 应用程序接口,但它们的设计方法却有本质区别。
Qt 3D C++ 和 QML 是实现自定义 3D 渲染器的灵活 API,为渲染过程的各个方面提供了底层抽象。因此,Qt 3D 为C++和QML 提供了 API。
相比之下,Qt Quick 3D 是一个高级 API,专注于 3D 内容的渲染,是Qt Quick 的扩展。因此,它的大部分 API 都基于 QML。因此,如果您当前的Qt 3D 应用程序严重依赖 C++,迁移到Qt Quick 3D 就需要过渡到 QML。
架构差异
Qt 3D Qt Quick 3D Qt Quick 3D 遵循与 相同的设计模式,并将其功能扩展到支持 3D 渲染。Qt Quick
Qt 3D 使用实体-组件系统(ECS)模式。在这个系统中,使用了一个通用的实体类,可以通过在实体中添加各种组件,按照组合模式对实体进行专门化。相比之下,Qt Quick 3D 使用基于继承的应用程序接口,与更广泛的 Qt 框架保持一致,其中专门的节点是一个通用基类的子类。由于这些根本性的差异,这两种应用程序接口之间并不存在直接的 1:1 映射。
Qt 3D 引入了 "方面"(Aspects)的概念,为连接到实体的各种类型的组件提供了不同的入口点。每个方面都可以创建由线程池处理的任务,按照提供者和依赖关系树完成渲染帧所需的所有工作。另一方面,Qt Quick 3D 与Qt Quick 使用相同的线程模型。它有一个前端 API(可从主 GUI 线程访问,用于管理场景状态)和一个位于呈现线程上的后端 API。虽然渲染线程可以利用其他线程完成某些任务,但从用户角度来看,概念模型只涉及两个线程。Qt Quick 3D 中没有明显的 "方面"(Aspects)概念,尽管Qt 3D 的 "方面 "提供的大部分功能仍然可用。
从渲染的角度来看,Qt 3D 提供了一种高度可定制的方法,允许创建定制的渲染管道。不过,这种灵活性也带来了复杂性;默认情况下,Qt 3D 不提供预配置的渲染管道。相比之下,Qt Quick 3D 的设计更加方便用户使用,其开箱即用的渲染管道可自动适应场景需求。例如,如果Qt Quick 3D 中的定向光设置为投射阴影,渲染管道将自动包含阴影贴图生成通道,然后场景中的材质将使用该通道。而在Qt 3D 中,这种功能需要手动配置帧图和扩展材质以支持阴影贴图。此外,对于不同的光线类型,如点光源或聚光灯,还需要进一步修改帧图和材质定义。虽然Qt 3D 为阴影贴图技术提供了强大的自定义选项,但与Qt Quick 3D 相比,这可能会造成很大的开销,因为在 中,启用阴影只需在灯光上设置一个属性即可。
每个模块的详细信息
Qt 3D 核心模块
Qt 3D 核心模块提供了建立其他Qt 3D 模块的基本基元。Qt 3D Core 中的大多数类在Qt Quick 3D 中都有对应的组件,但特别支持实体-组件-系统(ECS)架构的类除外。Qt Quick 3D 提供了与Qt 3D 中常用实体-组件组合相对应的预组装组件,从而简化了开发过程。
例如,这是 Qt3D 中可渲染实体的设置:
PhongMaterial { id: material } TorusMesh { id: torusMesh radius: 5 minorRadius: 1 rings: 100 slices: 20 } Transform { id: torusTransform scale3D: Qt.vector3d(1.5, 1, 0.5) rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45) } Entity { id: torusEntity components: [ torusMesh, material, torusTransform ] }
在上述代码中,您可以看到实体由网格、材质和变换组成。在Qt Quick 3D 中,相应的代码如下所示:
Model { id: torusModel scale: Qt.vector3d(1.5, 1, 0.5) eulerRotation.x: 45 geometry: TorusGeometry { radius: 5 tubeRadius: 1 rings: 100 segments: 20 } materials: [ PrincipledMaterial { id: material ] }
在此示例中,实体由Model 代替,它是Node 组件的子类,从Qt 3D 继承了Transform 组件的等价物。此外,任何 Node 子类都将是场景图的一部分,具有父子关系。模型组件是一个可呈现的实体,可以有几何体和材质。在本例中,我们使用geometry 属性来匹配Qt 3D 代码使用的过程 API,但也可以使用source 属性从静态文件中指定几何体。模型组件的materials 属性是用于对几何体进行阴影处理的材质列表。之所以是一个列表,是因为几何体可能包含多个子集,每个子集可能使用一种单独的材质。PrincipledMaterial 组件是一种 PBR(基于物理的渲染)材质,它使用金属粗糙度工作流程,在本示例中取代了Qt 3D 片段中使用的PhongMaterial 。
Qt 3D 输入
Qt 3D 通过自己的自定义输入系统提供一系列输入类。相比之下,Qt Quick 3D 主要重复使用Qt Quick 中的输入类。为了与三维场景交互,Qt Quick 3D 提供了额外的 API 来执行光线投射和对象拾取。
View3D 是Qt Quick 3D 中处理拾取操作的主要类,提供显式和隐式拾取方法。显式拾取 API 允许您通过在View3D 中指定 x 和 y 坐标,直接从 2D 空间拾取对象。该操作会返回一个pickResult 值,其中包含被拾取对象和拾取位置的详细信息。该应用程序接口是同步的,这意味着在射线投射后结果立即可用。此外,还可以从场景中的任意点施放射线。显式和隐式拾取方法都可以返回所有命中对象的列表,而不仅仅是第一个。
import QtQuick import QtQuick.Controls import QtQuick3D ApplicationWindow { width: 640 height: 480 visible: true View3D { id: view anchors.fill: parent PerspectiveCamera { z: 200 } DirectionalLight { } PrincipledMaterial { id: material baseColor: "red" } PrincipledMaterial { id: selectedMaterial baseColor: "blue" } Model { id: sphereModel source: "#Sphere" pickable: true materials: [ material ] } } MouseArea { anchors.fill: parent onClicked: (mouse) => { let result = view.pick(mouse.x, mouse.y) if (result.hitType == PickResult.Model) { if (result.objectHit == sphereModel) { // toggle selection if (sphereModel.materials[0] == material) sphereModel.materials[0] = selectedMaterial else sphereModel.materials[0] = material } } else { // deselect sphereModel.materials[0] = material } } } }
在本示例代码段中,创建了一个带有球体模型的简单场景。球体模型被设置为pickable ,这意味着当光线投射到场景中时,将考虑对该模型进行选取。在View3D 的顶部有一个MouseArea ,用于监听点击。当检测到点击时,就会调用pick 方法,并输入点击的 x 和 y 坐标。拾取操作的结果将用于确定球体模型是否被击中。如果被击中,球体模型的材质将在红色和蓝色材质之间切换。
import QtQuick import QtQuick.Controls import QtQuick3D ApplicationWindow { width: 640 height: 480 visible: true View3D { id: view anchors.fill: parent Node { eulerRotation.y: 45 PerspectiveCamera { z: 200 } } DirectionalLight { } Node { id: anchorItem Item { anchors.centerIn: parent width: 250 height: 250 Button { anchors.centerIn: parent text: "Click Me" } } } } }
在本示例代码片段中,创建了一个简单的场景,屏幕中央有一个Button 。按钮是一个 2D 项目,但由于它的父项目是Node ,因此它将被投射到 3D 空间。View3D 会自动处理向场景中投射光线,并将输入事件转发给场景中的任何 2D 项目。
import QtQuick import QtQuick.Controls import QtQuick3D ApplicationWindow { width: 640 height: 480 visible: true View3D { id: view anchors.fill: parent Node { eulerRotation.y: 25 PerspectiveCamera { z: 200 } DirectionalLight { } } Model { source: "#Cube" pickable: true materials: [ PrincipledMaterial { baseColorMap: Texture { sourceItem: Pane { width: 250 height: 250 Button { anchors.centerIn: parent text: "Click Me" } } } } ] } } }
同样,在上述代码片段中,创建了一个立方体Model ,其纹理是一个包含Button 的Pane 。在本例中,通过渲染一个包含Button 的Pane 组件,创建了一个 250x250 像素的Texture 。通常情况下,该纹理是非交互式的,但通过将Model 上的pickable 设置为 true,在上一示例中将输入事件转发至Button 的隐式拾取机制也将用于在本例中将输入事件转发至Button 。对于立方体Model ,立方体的每个面现在都将与Button 交互。
Qt 3D 中的大多数输入类都无法直接转换到Qt Quick 3D ,因此需要进行一些移植工作,但Qt Quick 3D 中的隐式拾取机制可以轻松地与 3D 场景中的 2D 项目进行交互。
Qt 3D 逻辑
Qt 3D Logic 模块包含一个单独的组件,为渲染的每一帧提供回调。在Qt Quick 3D 中,等效的方法是使用FrameAnimation 组件,这是一个通用的Qt Quick 组件,可为每个渲染帧提供定时信息和回调机制。
FrameAnimation { running: true onTriggered: { console.log(`currentFrame: ${currentFrame}, frameTime: ${frameTime}`) } }
该组件可用于场景中的任何地方,包括作为需要在每帧执行某些操作的对象的子对象。
值得注意的是,只有当您想在每一帧执行某些动作时,才需要使用这种模式。虽然FrameAnimation 可以在某些游戏引擎中重新创建轮询行为,但也可以在Qt Quick 3D 中使用与Qt Quick 相同的事件驱动模式。
Qt 3D 渲染
Qt 3D 渲染模块包含了渲染所需的大部分关键类。在Qt Quick 3D 中,大部分同等功能都是作为内部实现细节提供的,不会直接暴露给用户。不过,有一些类是作为 API 向用户公开的,可以用来扩展和定制Qt Quick 3D 的功能。
几何图形
Qt 3D 中的几何或网格数据由QGeometryRenderer 类或GeometryRenderer 组件表示。在Qt Quick 3D 中,对应的是QQuick3DGeometry 类和ProceduralMesh 组件。在Qt 3D 中,QGeometryRenderer 类的级别较低,希望用户提供顶点属性布局作为几何数据的一部分。在Qt Quick 3D 中,您可以控制使用哪些内置顶点属性,但缓冲区的布局是作为实现细节在内部处理的。
有关实现方法的详细信息,请查看Qt Quick 3D 自定义几何体示例。
纹理
Qt 3D 中的纹理由QAbstractTexture 的子类表示。在Qt Quick 3D 中,与之对应的是Texture 组件,它是纹理和采样器的前端表示。不过,纹理组件使用的数据有多种来源。最简单的方法是将source 属性设置为图像文件,然后将其作为纹理数据上传到 GPU。也可以使用二维 QML Runtime 内容作为源,或通过子类化QQuick3DTextureData 或使用ProceduralTextureData 组件,在运行时程序化地生成纹理数据。这些类型可让您通过提供大小、格式和原始图像数据来指定纹理数据。此外,还可以通过使用render extensions 在 GPU 上创建纹理。
有关创建方法的详细信息,请查看Qt Quick 3D 程序纹理示例。
材质
虽然可以在Qt 3D 中使用一些内置材质,但这样做时,您只能使用framegraphs 中的内置材质。Qt Quick 3D 的情况与此类似,它提供了一系列可与内部渲染策略配合使用的内置材质。不过,Qt 3D 和Qt Quick 3D 都可以创建自定义材质。
在Qt 3D 中,创建过程相当复杂,涉及QMaterial 、QEffect 、QTechnique 、QRenderPass 和QShaderProgram 树状结构。在Qt Quick 3D 中,创建材质的过程简化为一个CustomMaterial 组件。通过该组件,您可以为材质指定自定义着色器代码,其余设置由渲染器完成。CustomMaterial 有两种模式,着色模式和无着色模式。在着色模式下,自定义着色器代码 API 允许你自定义内置的PrincipledMaterial 着色器,而在无着色模式下,你可以从头开始编写自己的着色器代码。在这两种情况下,着色器语言都是 GLSL,并带有一些 Qt 特有的关键字扩展,以方便与Qt Quick 3D API 的其他部分集成。
有关如何实现的详细信息,请查看着色模式和无着色模式示例,以及有关使用Qt Quick 3D 自定义GLSL 着色器语言的概述。
效果
从 Qt3D 的角度来看,渲染 3D 场景与渲染后期处理特效之间并无区别,从低层次的角度来看,这是正确的。但是,Qt Quick 3D 。Models ,三维场景的一部分将包含一个materials 列表,这些将在主通道中渲染。Effects 的不同之处在于,在Qt Quick 3D 中,它们指的是后期处理效果,其中每个pass 最终都是覆盖整个渲染目标的单一四边形。然后,主色调通道生成的纹理将作为纹理传递给第一个特效通道,该通道的结果将作为纹理传递 给下一个Effect 通道,依此类推。然后将最后的效果传递渲染到输出渲染目标(通常是窗口表面)。有些Effects 需要在主要处理过程中创建buffers (如深度纹理),有些则需要中间步骤,所有这些都可以使用Qt Quick 3D 中的效果 API 来定义。
Qt Quick 3D 使用 API 专门设计这个后期处理效果链,您可以根据需要定义尽可能多的渲染通道,以达到所需的效果。
如需了解更多详情,请查看Qt Quick 3D 后期处理示例。
实例缓冲区
在Qt 3D 中,实例化和实例缓冲区的使用比较低级,因此如何使用取决于具体的使用情况。在Qt Quick 3D 中,内置材质有一些固定的实例化属性可以设置,但要设置这些属性,需要以缓冲区的形式提供实例数据。有几种方法可以提供这种数据,一旦提供,只需在Model 组件上设置instancing 属性,即可将实例缓冲区数据与要实例化的内容关联起来。
更多详情,请查看Qt Quick 3D 中有关实例化的文章。
渲染器扩展
Qt 3D 的目的是提供一种功能强大的方法来为您的应用程序定义自定义渲染解决方案。Qt Quick 3D 并不提供这种级别的自定义,因为它的目标是易于使用而不是易于自定义,但除了上述所有方法外,它还提供了一些扩展渲染管道的方法。扩展方法是通过实现render extensions ,它允许你使用与渲染器相同的数据向渲染器添加自定义渲染通道。这是一个低级 API,需要充分了解Qt Quick 3D 渲染管道以及Qt Rendering Hardware Interface (RHI) API。
有关如何实现的示例,请查看模板轮廓扩展示例
Qt 3D 额外功能
Qt 3D Extras 模块提供了各种实用工具,如材质、几何体生成器和相机控制器,以方便用户获得 "开箱即用 "的体验。它还包括一个前向渲染器帧图。在Qt Quick 3D 中,您无需明确定义帧图;相反,帧图会根据场景要求自动生成。
内置材质
在Qt Quick 3D 中,可通过使用PrincipledMaterial 或SpecularGlossyMaterial 或使用自定义着色器代码定义CustomMaterial 来提供材质。PrincipledMaterial 是使用金属-粗糙度工作流程的 PBR(基于物理的渲染)着色器。根据用户设置的属性和场景内容,生成着色器的复杂度会增加。例如,如果灯光投射阴影,生成的着色器将包含阴影接收代码;如果存在探照灯或反射探照灯,着色器将包含照明信息。这种动态适应也适用于阴影模式下的CustomMaterial 。但是,未着色的CustomMaterial 着色器不会自动考虑照明等场景信息。
几何辅助工具
与Qt 3D 类似,Qt Quick 3D 也提供了许多内置几何基元以及许多程序化几何生成器。
下表提供了Qt 3D 中几何图形类与Qt Quick 3D 中相应类的映射:
Qt 3D | Qt Quick 3D |
---|---|
ConeMesh | ConeGeometry |
CuboidMesh | CuboidGeometry |
CylinderMesh | CylinderGeometry |
ExtrudedTextMesh | ExtrudedTextGeometry |
PlaneMesh | PlaneGeometry |
SphereMesh | SphereGeometry |
TorusMesh | TorusGeometry |
摄像机控制器
在相机控制方面,Qt Quick 3D 提供了与OrbitCameraController 和WasdController 类似的选项。WasdController 与 Qt3D 中的FirstPersonCameraController 相似,但它可以控制场景中的任何项目,而不仅仅是摄像头。
Qt 3D | Qt Quick 3D |
---|---|
FirstPersonCameraController | WasdController |
OrbitCameraController | OrbitCameraController |
Qt 3D 动画
Qt 3D 动画模块定义了如何在 3D 中处理不同类型的动画。在Qt Quick 3D 中,等同功能分布在多个模块中,通常会尽可能利用Qt Quick 中的现有类。例如,您可以使用QtQuick 导入的各种动画类对 3D 节点的属性进行动画处理。
从三维内容创建工具导入动画时,通常使用时间线来定义。该 Qt Quick Timeline模块提供了定义此类动画所需的类,任何带有动画的导入内容都需要使用该模块才能运行。
除了对场景中的项目进行基本变换(如平移、旋转和缩放)外,Qt Quick 3D 还支持基于电枢的动画,即对骨架中的骨骼进行动画,影响与每个关节或骨骼相关的顶点。这是通过Skin 组件实现的,该组件将代表场景中骨骼的Node 对象与Model's 几何体中相应的骨骼权重连接起来。在这种情况下,骨架动画是通过使用变换对骨骼进行动画处理来实现的。
Qt Quick 3D 此外,《CGI》还支持变形目标动画,可直接将模型几何体的变化制作成动画。变形目标是模型几何体的预定义快照,您可以使用MorphTarget 组件在它们之间制作动画。
要组合多个动画或将它们混合在一起,可以使用Qt Quick Timeline 混合树模块中的组件。通过该模块,您可以定义复杂的树来管理不同动画的交互方式。这些通用动画类大多不是Qt Quick 3D 特有的,而是更广泛的Qt Quick 框架的一部分,它们利用了现有的功能,而不是引入新的方法。
Qt 3D 场景 2D
在Qt 3D 中,Scene2D 组件用于将二维Qt Quick 场景渲染为可在三维场景中使用的纹理。在Qt Quick 3D 中,这是通过Texture 组件的sourceItem 属性实现的。sourceItem 属性可引用现有的Item 或定义一个内联项。顶层项决定纹理大小,Qt Quick 场景渲染到映射到Texture 组件的图层上。
此外,还可以在三维场景中直接使用Item-based 组件,无需纹理即可将其投射到三维空间中。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.