Back to Blog home

Qt Quick 3D介绍:Qt Quick的高级3D API

Published on Tuesday October 22, 2019 by Richard Lin in Graphics OpenGL Qt Quick 3D qt-blogs-chinese qt-labs-chinese qt-quarterly-chinese qtearth-blogs-chinese 嵌入式 | Comments

本文翻译自:Introducing Qt Quick 3D: A high-level 3D API for Qt Quick
原文作者:Andy Nichols
校审:王富涌、Richard Lin

正如Lars在他的Qt 6技术概览一文中提到的,我们一直在研究如何在3D和Qt Quick之间进行更深入的集成。因此,我们创建了一个名为Qt Quick 3D的新项目,提供高级API,用于从Qt Quick为用户界面创建3D内容。 我们没有使用会导致动画同步问题和需要多层抽象的外部引擎,而是在Qt Quick Scenegraph中扩展了3D的部分,并为这些扩展的场景图节点(scene graph node)提供了对应的渲染器。

这并不意味着我们为Qt编写了另一个3D解决方案,因为核心的场景渲染器是从Qt 3D Studio的渲染器派生出来的。这个渲染器被移植过来,使用Qt的平台抽象层,并被重构以满足Qt项目的编码风格。

Complex 3D scene visualized with Qt Quick 3D
在Qt Quick 3D中运行的“San Miguel”测试场景

我们的目标是什么?为什么提出另一个3D解决方案?

统一图形技术

我们最重要的目标是统一我们的图形技术。目前,我们为创建流畅的用户界面提供了两种综合解决方案来,每个解决方案都有自己的相应工具。其中一个解决方案是处理2D的Qt Quick,另一个是用于3D的Qt 3D Studio。如果只使用其中一种,事情通常会很顺利。然而,我们发现用户通常需要混合使用,这在运行时的性能以及开发者/设计师体验方面都会造成许多问题。
因此,为了简便起见,我们的目标是拥有一个运行时(Qt Quick)、一个通用场景图(Qt Quick Scenegraph)和一个设计工具(Qt Design Studio)。而且不会对特性、性能或开发者/设计师的体验造成任何影响。这样,我们就不需要在复杂的产品线中分散我们的开发焦点,可以更快地添加更多特性和修复bug。

直观易用的API

Qt Quick 3D的下一个目标是提供一个用于定义3D内容的API,开发者不需要了解现代图形管道的复杂细节就可以使用这个API。毕竟,大多数用户不需要为每个应用程序创建专门的3D图形渲染器,而只是想显示一些3D内容,通常是与2D一起显示。因此,我们在开发Qt Quick 3D的过程中一直牢记这一点。
也就是说,接下来我们将会公布越来越多的渲染API,这将使高级用户可以实现更高级的用例。
在撰写本文时,我们只提供了一个QML API,但是我们的目标是提供一个公共的C++ API。

Qt Quick的统一工具

Qt Quick 3D旨在成为Qt 3D Studio的继承者。目前Qt 3D Studio仍将继续开发,但从长远来看,将被Qt Quick和Qt Design Studio所取代。
在这里,我们打算把Qt 3D Studio最好的部分融合到Qt Quick和Qt Design Studio中。不再需要为Qt Quick或3D提供单独的工具,而是在Qt Design Studio中同时完成这两项工作。我们现在正在研究这方面的细节,希望不久就能发布预览版。
对于Qt 3D Studio的现有用户,我们一直在开发一个移植工具,可以将项目转换为Qt Quick 3D。稍后会有详细介绍。

一流的素材优化管道

在处理3D场景时,素材优化正日益重要,因为现在的图形素材类型越来越多,而且体量也越来越大。因此,作为Qt Quick 3D开发工作的一部分,我们一直在研究如何尽可能方便地导入您的数据,并将其转换为Qt Quick中使用的,高效的运行时格式。
例如,在设计时,您会使用创建工具生成的图形素材(例如,Maya中处理3D模型的FBX文件,或者Photoshop中处理纹理的PSD文件),但是在运行时,您就不会希望引擎使用这些格式。取而代之,您希望将素材转换成某种高效的运行时格式,并在每次源设计修改时更新它们。我们希望将这一过程尽可能自动化,因此希望将它们整合到Qt构建系统和工具中。

跨平台性能和兼容性

我们的另一个目标是通过Qt新的渲染硬件接口(Rendering Hardware Interface,缩写RHI),使它支持多种原生图形API。目前,Qt Quick 3D如Qt中其他许多组件一样,只能使用OpenGL渲染。但是在Qt 6中,我们将使用QtRHI作为我们的图形抽象层,除了OpenGL之外,我们还将支持Vulkan、Metal和Direct3D渲染。

Qt Quick 3D到底是什么?

Qt Quick 3D的目标不是替代Qt 3D,而是Qt Quick的功能性扩展,渲染3D内容的高级API。
下面是一个非常简单的项目,附带注释:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick3D 1.0

Window {
id: window
visible: true
width: 1280
height: 720

// Viewport for 3D content
View3D {
id: view

anchors.fill: parent
// Scene to view
Node {
id: scene

Light {

id: directionalLight

}

Camera {
id: camera
// It's important that your camera is not inside
// your model so move it back along the z axis
// The Camera is implicitly facing up the z axis,
// so we should be looking towards (0, 0, 0)
z: -600
}

Model {
id: cubeModel
// #Cube is one of the "built-in" primitive meshes
// Other Options are:
// #Cone, #Sphere, #Cylinder, #Rectangle
source: "#Cube"

// When using a Model, it is not enough to have a
// mesh source (ie "#Cube")
// You also need to define what material to shade
// the mesh with. A Model can be built up of
// multiple sub-meshes, so each mesh needs its own
// material. Materials are defined in an array,
// and order reflects which mesh to shade

// All of the default primitive meshes contain one
// sub-mesh, so you only need 1 material.

materials: [

DefaultMaterial {

// We are using the DefaultMaterial which
// dynamically generates a shader based on what
// properties are set. This means you don't
// need to write any shader code yourself.
// In this case we just want the cube to have
// a red diffuse color.
id: cubeMaterial
diffuseColor: "red"
}
]
}
}
}
}

其理念是,定义3D内容应该像定义2D内容一样简单。您需要一些额外的东西,比如灯光、摄像机和材质等概念,但是所有这些都是高级场景概念,而不是图形管道的实现细节。
当然,简单的API会导致功能上的限制。尽管可以定制材质和场景的内容,但不可能完全定制场景的渲染,这与Qt 3D中使用的高度可定制的framegraph不同。Qt Quick 3D用一个固定的正向渲染器,您可以用属性来定义如何渲染场景中的对象。这与其他现有的引擎类似,通常有一些渲染管道可供选择,然后使用这些管道渲染逻辑场景。

Camera Orbiting Car
在一个带有轴线和网格线的天空盒中,摄像机围绕着一个汽车模型旋转(备注:卡顿的原因是其为一个12 FPS的GIF图像)。

您能用Qt Quick 3D做什么?

它可以做很多事情,但是这些都是使用以下场景元素构建的:

节点/Node

节点是3D场景中的基本组件。它代表了3D空间中的坐标变换,但它是不可见的。其工作原理类似于Qt Quick中的Item类型

摄像机/Camera

摄像机表示场景是如何投影到2D表面的。相机在3D空间中有一个位置(因为它是节点子类)和一个投影。要渲染场景,您至少需要有一台摄像机。

光照/Light

光照组件定义了场景中的光源,照亮了考虑照明的所有材质。现在,有三种类型的灯光:指向型光源(默认)、点光源和面光源。

模型/Model

模型组件是场景中的一个可视化组件。它代表几何图形(从网格)和一种或多种材质的组合。
网格组件的源属性需要一个. mesh文件,这是Qt Quick 3D使用的运行时格式。要获取网格文件,您需要使用素材导入工具转换3D模型。还有一些内置元素。需要设置以下源属性:#Cube, #Cylinder, #Sphere, #Cone, 或 #Rectangle.
我们还将添加一种可编程的方式,在运行时动态添加自定义模型组件,但它在预览版本中暂时不可用。
在渲染模型之前,它还必须要有材质。这里定义了网格的着色方式。

DefaultMaterial 和 Custom Materials

DefaultMaterial组件是一种易于使用的内置材质。您所需要做的就是创建这个材质,设置您想要定义的属性,后台会自动生成所有必要的着色器代码。您在场景中设置的所有其他属性也会被考虑在内。不需要自己编写任何图形着色器代码(如顶点着色器或片段着色器)。
也可以使用所谓的CustomMaterials,您可以在其中提供自己的着色器代码。我们还提供了一个预定义的定制材料库,您只需在QML中添加以下内容即可试用:

import QtQuick3D.MaterialLibrary 1.0

纹理/Texture

纹理组件表示3D场景中的纹理,以及它是如何映射到网格的。纹理的来源可以是图像文件,也可以是QML组件。

可用功能示例

Qt Quick内置的3D视图

要查看Qt Quick内部的3D内容,有必要将其展平为2D曲面。为此,您可以使用View3D组件。View3D是整个API中唯一基于QQuickItem的组件。您可以将场景定义为View3D的子元件,也可以将scene属性设置为要渲染的场景的根节点来引用现有场景。
如果您有多台摄像机,也可以设置要使用哪台摄像机来渲染场景。默认情况下,它将只使用场景中定义的第一个活动摄像机。
在渲染之前值得注意的是,View3D 元素不一定要渲染到离屏纹理(off-screen texture)。可以设置以下四种渲染模式之一来定义如何渲染3D内容:

  1. Texture:View3D是Qt Qtuick纹理提供者,通过FBO将内容渲染成纹理
  2. Underlay:View3D在Qt Quick的2D内容渲染之前被直接渲染到窗口中(3D始终位于2D下方)
  3. Overlay:View3D是在Qt Quick的2D内容渲染后渲染到窗口中的(3D总是在2D之上)
  4. RenderNode:View3D与Qt Qtuick 2D内容同时渲染。由于Qt 5的Qt Quick 2D使用了深度缓冲区,可能会导致一些问题。

3D中的2D视图

可能您想在3D场景中渲染Qt Quick内容。为此,在任何将纹理作为属性值的地方(例如,在默认材质的diffuseMap属性中),您都可以使用Texture元件,该元件设置了sourceItem属性,而不仅仅是指定文件。这样,引用的Qt Quick 元素将被自动渲染并用作纹理。

animated_cubes

映射到立方体的漫反射颜色纹理是动态的Quick 2D元素。

3D QML组件

由于Qt Quick 3D是基于QML构建的,因此也可以为3D创建可重用的组件。例如,如果您创建了一个包含多个模型组成的汽车模型,只需将其保存到Car.qml中,然后您就可以通过重用它来实例化多个汽车实例,就像任何其他QML类型一样。这非常重要,因为这样可以使用相同的组件模型创建2D和3D场景,而不必为2D和3D场景提供不同的处理方法。

同一场景的多个视图

因为场景定义可以存在于Qt Quick项目中的任何地方,所以可以从多个View3D中引用它们。如果场景中有多台摄像机,甚至可以从每台摄像机渲染到不同的View3D。

teapots

同一茶壶场景的四个视图。在投影视图中也在三台摄像机之间变化。

阴影/Shadow

任何光照组件都可以指定它正在投射的阴影。启用此选项后,场景中将自动渲染阴影。根据您正在做的事情,渲染阴影可能非常耗时,因此您可以通过在模型上设置附加属性来微调哪些模型组件投射和接收阴影。

基于图像的光照/ Image Based Lighting

除了标准光照组件之外,还可以通过定义HDRI地图来照亮场景。该纹理可以在其场景环境属性中为整个View3D设置,也可以在单个材质上设置。

动画/ Animations

Qt Quick 3D中的动画使用与Qt Quick相同的动画系统。您可以将任何属性绑定到animator上,它将按照以往那样运动和更新。通过QtQuickTimeline模块,也可以使用基于关键帧的动画。
像组件模型一样,这是缩小2D和3D场景之间差距的另一个重要步骤,因为这里没有使用独立的、可能冲突的动画系统。
目前不支持rigged animation,但会纳入未来的计划。

如何亲自尝试?

我们的目标是在发布Qt 5.14的同时,发布Qt Quick 3D的技术预览版本。与此同时,可以在Qt 5.12和之后版本上使用。
要获取代码,您只需要从以下位置获取并编译QtQuick3D模块:

https://git.qt.io/annichol/qtquick3d

关于工具

我们的目标是可以完全使用Qt Design Studio来建立3D场景。这意味着能够直观地布局场景,导入网格、材质和纹理等3D素材,并将这些素材转换为引擎使用的高性能的运行时格式。

designstudio3d
Qt Quick 3D和Qt Design Studio早期的集成演示

将3D场景导入QML组件

Qt Quick 3D也可以通过手动编写QML代码来使用。同时我们也有一些独立的素材转换工具。过去这些工具还不成熟。现在可以使用3D素材创建工具(如Blender、Maya或3DS Max)为该实用工具提供素材,它将生成一个代表这个场景的QML组件,以及它使用的所有纹理、网格和材质。目前,该工具支持从以下格式生成场景:

  • FBX
  • Collada (dae)
  • OBJ
  • Blender (blend)
  • GLTF2

要转换文件myTestScene.fbx,您需要运行:

./balsam -o ~/exportDirectory myTestScene.fbx

这将生成一个名为MyTestScene.qml的文件以及所有需要的素材。然后,您可以像场景中的任何其他组件一样使用它:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick3D 1.0

Window {
width: 1920
height: 1080
visible: true
color: "black"

Node {
id: sceneRoot
Light {
}
Camera {
z: -100
}
MyTestScene {
}
}

View3D {
anchors.fill: parent
scene: sceneRoot
}
}

我们正在努力改进该工具生成的素材,希望在未来几个月内实现改进。

转换Qt 3D Studio项目

除了能够从3D素材创建工具生成3D QML组件,我们还为素材导入工具创建了插件,用于转换现有的Qt 3D Studio项目。如果您以前使用过Qt 3D Studio,您应该知道它会以XML格式生成项目来定义场景。如果您给该工具一个由Qt 3D Studio生成的UIP或UIA项目,它也将在此基础上生成一个Qt Quick 3D项目。但是请注意,由于Qt 3D Studio使用的运行时不同于Qt Quick 3D,并非所有内容都将被转换。尽管如此,它应该为转换现有项目提供了一个很好的起点。我们希望继续改善对这种方法的支持,以使现有的Qt 3D Studio用户顺利过渡。

qt3dstudio_sample

使用Qt Quick 3D导入工具移植的Qt 3D Studio示例应用程序。(它还不完美)

关于Qt 3D

我估计听到的第一个问题是为什么不直接使用Qt 3D?这也是我们过去几年一直在探索的问题。
一个自然的假设是,如果我们想混合2D和3D,我们可以在Qt 3D的基础上构建所有的Qt Quick。我们本来也是这么想的,而且在Qt 3D Studio 2.3版本中这样做。Qt 3D作为一个很强大的3D引擎在Qt Quick和Qt 3D Studio中被使用。但是,Qt 3D的架构使得我们很难在入门级嵌入式硬件上获得所需的性能。Qt 3D也有一定的开销,这来自于自身较慢的运行时,也来自于Qt Quick和图形硬件之间的另一个抽象层。就目前的形式而言,如果我们想要在确保对从低端到高端的各种平台和设备持续提供良好支持的同时,实现完全统一的图形技术,Qt 3D并不是理想的构建基础。
与此同时,我们已经在Qt 3D Studio中有了一个渲染引擎,它可以完全满足我们的需求,是构建额外功能的良好基础。这样做的缺点是不再拥有Qt 3D带来的许多强大API,但是实际上一旦您开始在Qt 3D之上构建一个运行时,您就已经决定了在能力有限的定制框架上的工作方式。最后,最实际的决定是使用现有的Qt 3D Studio渲染引擎作为我们的基础,并以此为基础进行构建。

未来的计划是什么?

本文只是未来的概览。计划是将Qt Quick 3D作为一个完整的模块在Qt 5.15 LTS版本中提供。现在我们正在进一步开发Qt Quick 3D,并将在Qt 5.14中作为技术预览版发布。
对于Qt 5系列,由于二进制兼容性的承诺,我们限制了2D和3D的结合深度。随着Qt 6的发布,我们计划将Qt Quick 3D更深入地集成到Qt Quick中,以提供更流畅的体验。
我们的目标是,希望能够尽可能高效地混合2D和3D内容,而不会给不使用3D内容的用户带来任何额外开销。我们不会做任何极端的事情,比如强迫所有Qt Quick应用程序使用新的渲染器,只有那些混合了2D和3D的应用程序才需要。
在Qt 6中,我们还将使用QtRHI来渲染Qt Quick (包括3D)场景,这将消除我们目前在部署OpenGL应用程序时遇到的许多问题(通过在窗口上使用DirectX,在macOS上使用Metal等等)。)。
我们还希望最终用户能够使用我们创建的更通用的C++渲染API,而不仅是Qt Quick。代码现在作为私有应用程序接口存在,但是我们要等到Qt 6的时候(RHI移植)之后,才能做出API的兼容性承诺。

非常期待您的反馈!

这是一个技术预览,您现在看到的很多东西都有可能会发生改变。例如,现在的API有些粗糙,所以我们想知道我们遗漏了什么,哪些是毫无意义的,哪些是有效的,哪些是无效的。提供这种反馈的最佳方式是通过Qt Bug Tracker。记得在提交问题或者建议时使用Qt Quick:3D标签。

Subscribe to Our Blog

Stay up to date with the latest marketing, sales and service tips and news.