将Qt 3D 移植到Qt Quick 3D :迁移指南
本指南面向目前正在使用 Qt 3D 并希望迁移到 Qt Quick 3D。
适用范围的差异
虽然 Qt 3D 和 Qt Quick 3D 虽然都提供了3D API,看似相似,但两者的设计理念存在根本性的差异。
Qt 3D 是一个用于实现自定义 3D 渲染器的灵活 API,为渲染过程的各个方面提供了低级抽象。因此,Qt 3D 同时提供了C++和QML 的 API。
相比之下,Qt Quick 3D 是一个专注于 3D 内容渲染的高级 API,并作为Qt Quick 的扩展而设计。因此,其大部分 API 都基于 QML。所以,如果您的当前Qt 3D 应用程序高度依赖 C++,那么迁移到Qt Quick 3D 将需要转向使用 QML。
架构差异
Qt 3D 与Qt Quick 3D 在架构上存在显著差异。Qt Quick 3D 遵循与Qt Quick 相同的设计模式,并扩展了其功能以支持 3D 渲染。
第一个主要区别在于设计模式的使用。Qt 3D 采用实体-组件系统(ECS)模式。在此系统中,使用一个通用的实体类,可以通过向实体添加各种组件来对其进行特化,遵循组合模式。 相比之下,Qt Quick 3D 采用基于继承的 API,这与更广泛的 Qt 框架保持一致——其中,特定类型的节点是共同基类的子类。由于这些根本性的差异,这两个 API 之间不存在直接的 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 核心中的大多数类在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子类都将成为场景图的一部分,并具有父子关系。 Model组件是一个可渲染实体,可以拥有几何体和材质。在此示例中,我们使用geometry 属性以匹配Qt 3D 代码所使用的过程化API,但也可以通过source 属性从静态文件中指定几何体。Model组件的materials 属性是一个用于为几何体着色的材质列表。 之所以采用列表形式,是因为几何体可能包含多个子集,每个子集都可能使用不同的材质。PrincipledMaterial 组件是一种基于物理渲染(PBR)的材质,它采用金属-粗糙度(Metallic-Roughness)工作流,在本例中,它取代了Qt 3D 代码片段中使用的PhongMaterial 。
Qt 3D 输入
Qt 3D 通过其自定义输入系统提供了一系列输入类。相比之下,Qt Quick 3D 主要复用来自Qt Quick 的输入类。为了与 3D 场景交互,Qt Quick 3D 提供了额外的 API 来执行光线投射和对象拾取操作。
View3D 是Qt Quick 3D 中处理拾取操作的主类,同时提供了显式和隐式拾取方法。显式拾取 API 允许您通过在View3D 中指定 x 和 y 坐标,直接从 2D 空间进行拾取。该操作会返回一个pickResult 值,其中包含关于被击中对象及其击中位置的详细信息。 该 API 为同步操作,这意味着在投射光线后可立即获得结果。此外,可以从场景中的任意点投射光线。显式和隐式拾取方法均可返回所有命中对象的列表,而不仅仅是第一个。
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 。 Button 虽然是一个 2D 对象,但由于其父对象是Node ,因此它会被投影到 3D 空间中。此类组件的交互方式与 2D 空间中的操作完全相同。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。 此外,还可以通过在运行时程序化生成纹理数据,具体方法包括:使用 2D QML 内容作为源、继承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 作为 3D 场景的一部分将包含一组materials ,这些将在主渲染阶段中进行渲染。 这些主渲染通道的结果是将 3D 内容直接渲染到窗口表面或纹理上。Effects 则有所不同,在Qt Quick 3D 中,它们指的是后处理效果,其中每个pass 最终都会成为一个覆盖整个渲染目标的单一四边形。 主颜色渲染通道生成的纹理随后作为纹理传递给第一个效果通道,该通道的结果再作为纹理传递给下一个Effect 效果通道,依此类推。 最后一个渲染通道会被渲染到输出渲染目标上,通常是窗口表面。某些Effects 需要主渲染通道期间创建的buffers ,例如深度纹理,而其他效果则需要中间步骤,所有这些都可以使用Qt Quick 3D 中的 Effects 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 的“扩展”模块提供了各种实用工具,以实现“开箱即用”的体验,例如材质、几何体生成器和摄像机控制器。它还包含一个前向渲染器的帧图。在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 |
| 球体网格 | SphereGeometry |
| 环面网格 | 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节点的属性进行动画处理。
从 3D 内容创作工具导入动画时,这些动画通常是通过时间线定义的。 Qt Quick Timeline 模块提供了定义此类动画所需的类,任何包含动画的导入内容都需要该模块才能正常运行。
除了场景中对象的基本变换(如平移、旋转和缩放)外,Qt Quick 3D 还支持基于骨骼的动画,即通过动画控制骨骼中的骨骼,从而影响与每个关节或骨骼相关的顶点。这是通过Skin 组件实现的,该组件将场景中代表骨骼的Node 对象与Model's 几何体中的相应骨骼权重关联起来。 在此情况下,对骨骼进行动画处理是通过使用变换来对骨骼进行动画处理来实现的。
Qt Quick 3D 还支持形态目标动画,您可以通过该功能直接对模型的几何体变化进行动画处理。形态目标是模型几何体的预定义快照,您可以使用“MorphTarget ”组件在这些快照之间进行动画过渡。
若要组合多个动画或将其混合,则使用Qt Quick Timeline 的Blend Trees模块中的组件。该模块允许您定义复杂的树结构,以管理不同动画之间的交互方式。这些通用的动画类大多并非Qt Quick 3D 所独有,而是更广泛的Qt Quick 框架的一部分,它们利用现有功能而非引入新方法。
Qt 3D Scene2D
在Qt 3D 中,Scene2D组件用于将2DQt Quick 场景渲染为纹理,以便在3D场景中使用。 在Qt Quick 3D 中,这是通过Texture 组件的sourceItem 属性实现的。sourceItem 属性可以引用现有的Item ,也可以定义一个内联项。顶级项决定了纹理大小,而Qt Quick 场景被渲染到一个映射到Texture 组件的图层上。
此外,还可以在 3D 场景中直接使用Item-based 组件,这些组件会在 3D 空间中进行投影,而无需纹理。
© 2026 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.