Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtquick3d-assetintro.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2023 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
6
\title Qt Quick 3D Introduction with glTF Assets
7
\page quick3d-asset-intro
8
9
The \l{Qt Quick 3D - Introduction} example provides a quick introduction to
10
creating QML-based applications with Qt Quick 3D, but it does so using only
11
built-in primitives, such as spheres and cylinders. This page provides an
12
introduction using \l{https://en.wikipedia.org/wiki/GlTF#glTF_2.0}{glTF 2.0}
13
assets, using some of the models from the
14
\l{https://github.com/KhronosGroup/glTF-Sample-Models}{Khronos glTF Sample
15
Models repository}.
16
17
\section1 Our Skeleton Application
18
19
Let's start with the following application. This code snippet is runnable as-is
20
with the \c qml command-line tool. The result is a very green 3D view with
21
nothing else in it.
22
23
\qml
24
import QtQuick
25
import QtQuick3D
26
import QtQuick3D.Helpers
27
28
Item {
29
width: 1280
30
height: 720
31
32
View3D {
33
anchors.fill: parent
34
35
environment: SceneEnvironment {
36
backgroundMode: SceneEnvironment.Color
37
clearColor: "green"
38
}
39
40
PerspectiveCamera {
41
id: camera
42
}
43
44
WasdController {
45
controlledObject: camera
46
}
47
}
48
}
49
\endqml
50
51
\image assetintro_empty.jpg
52
53
\section1 Importing an Asset
54
55
We are going to use two glTF 2.0 models from the Sample Models repository:
56
Sponza and Suzanne.
57
58
These models typically come with a number of texture maps and the mesh
59
(geometry) data stored in a separate binary file, in addition to the .gltf
60
file:
61
62
\image assetintro_sponza_dir.jpg
63
64
How do we get all this into our Qt Quick 3D scene?
65
66
There are a number of options:
67
68
\list
69
70
\li Generate QML components that can be instantiated in the scene. The
71
command-line tool to perform this conversion is the \l{Balsam Asset Import
72
Tool}{Balsam} tool. Besides generating a .qml file, that is effectively a
73
subscene, this also repacks the mesh (geometry) data into an optimized,
74
fast-to-load format, and copies the texture map image files as well.
75
76
\li Perform the same using \c balsamui, a GUI frontend for \l{Balsam Asset
77
Import Tool}{Balsam}.
78
79
\li If using \l{Qt Design Studio},
80
the asset import process is integrated into the visual design tools. Importing
81
can be triggered, for example, by dragging and dropping the .gltf file onto the
82
appropriate panel.
83
84
\li For glTF 2.0 assets in particular, there is a runtime option as well: the
85
\l RuntimeLoader type. This allows loading a .gltf file (and the associated
86
binary and texture data files) at runtime, without doing any pre-processing via
87
tools such as \l{Balsam Asset Import Tool}{Balsam}. This is very handy in
88
applications that wish to open and load user-provided assets. On the other
89
hand, this approach is significantly less efficient when it comes to
90
performance. Therefore, we will not be focusing on this approach in this
91
introduction. Check the \l{Qt Quick 3D - RuntimeLoader Example} for an example
92
of this approach.
93
94
\endlist
95
96
Both the \c balsam and \c balsamui applications are shipped with Qt, and should
97
be present in the directory with other similar executable tools, assuming Qt
98
Quick 3D is installed or built. In many cases, running \l{Balsam Asset Import
99
Tool}{balsam} from the command-line on the .gltf file is sufficient, without
100
having to specify any additional arguments. It is worth being aware however of
101
the many command-line, or interactive if using \c balsamui or Qt Design Studio,
102
options. For example, when working with \l{quick3d-lightmap}{baked lightmaps to
103
provide static global illumination}, it is likely that one will want to pass
104
\c{--generateLightmapUV} to get the additional lightmap UV channel generated at
105
asset import time, instead of performing this potentially consuming process at
106
run-time. Similarly, \c{--generateMeshLevelsOfDetail} is essential when it is
107
desirable to have simplified versions of the meshes generated in order to have
108
\l{Qt Quick 3D Level of Detail}{automatic LOD} enabled in the scene. Other options
109
allow generating missing data (e.g. \c{--generateNormals}) and performing various
110
optimizations.
111
112
In \c balsamui the command-line options are mapped to interactive elements:
113
\image assetintro_balsamui_options.jpg
114
115
\section2 Importing via balsam
116
117
Let's get started! Assuming that the
118
\l{https://github.com/KhronosGroup/glTF-Sample-Models} \c git repository is
119
checked out somewhere, we can simply run balsam from our example application
120
directory, by specifying an absolute path to the .gltf files:
121
122
\c{balsam c:\work\glTF-Sample-Models\2.0\Sponza\glTF\Sponza.gltf}
123
124
This gives us a \c{Sponza.qml}, a \c{.mesh} file under the \c meshes
125
subdirectory, and the texture maps copied under \c maps.
126
127
\note This qml file is not runnable on its own. It is a \e component, that
128
should be instantiated within a 3D scene associated with a \l View3D.
129
130
Our project structure is very simple here, as the asset qml files live right
131
next to our main .qml scene. This allows us to simply instantiate the Sponza
132
type using the \l{QML Documents}{standard QML component system}. (at run-time
133
this will then look for Sponza.qml in the filesystem)
134
135
Just adding the model (subscene) is pointless however, since by default the
136
materials feature the full \l{quick3d-pbr}{PBR lighting calculations}, so
137
nothing is shown from our scene without a light such as \l DirectionalLight, \l
138
PointLight, or \l SpotLight, or having \l{Using Image-Based
139
Lighting}{image-based lighting} enabled via
140
\l{SceneEnvironment::lightProbe}{the environment}.
141
142
For now, we choose to add a DirectionalLight with the default settings. (meaning
143
the color is \c white, and the light emits in the direction of the Z axis)
144
145
\qml
146
import QtQuick
147
import QtQuick3D
148
import QtQuick3D.Helpers
149
150
Item {
151
width: 1280
152
height: 720
153
154
View3D {
155
anchors.fill: parent
156
157
environment: SceneEnvironment {
158
backgroundMode: SceneEnvironment.Color
159
clearColor: "green"
160
}
161
162
PerspectiveCamera {
163
id: camera
164
}
165
166
DirectionalLight {
167
}
168
169
Sponza {
170
}
171
172
WasdController {
173
controlledObject: camera
174
}
175
}
176
}
177
\endqml
178
179
Running this with the \c qml tool will load and run, but the scene is all empty
180
by default since the Sponza model is behind the camera. The scale is also not
181
ideal, e.g. moving around with WASD keys and the mouse (enabled by the
182
\l WasdController) does not feel right.
183
184
To remedy this, we scale the Sponza model (subscene) by \c 100 along the X, Y,
185
and Z axis. In addition, the camera's starting Y position is bumped to 100.
186
187
\qml
188
import QtQuick
189
import QtQuick3D
190
import QtQuick3D.Helpers
191
192
Item {
193
width: 1280
194
height: 720
195
196
View3D {
197
anchors.fill: parent
198
199
environment: SceneEnvironment {
200
backgroundMode: SceneEnvironment.Color
201
clearColor: "green"
202
}
203
204
PerspectiveCamera {
205
id: camera
206
y: 100
207
}
208
209
DirectionalLight {
210
}
211
212
Sponza {
213
scale: Qt.vector3d(100, 100, 100)
214
}
215
216
WasdController {
217
controlledObject: camera
218
}
219
}
220
}
221
\endqml
222
223
Running this gives us:
224
225
\image assetintro_sponza_first.jpg
226
227
With the mouse and the WASDRF keys we can move around:
228
229
\image assetintro_sponza_second.jpg
230
231
\image assetintro_sponza_out.jpg
232
233
\note We mentioned \c{subscene} a number of times above as an alternative to
234
"model". Why is this? While not obvious with the Sponza asset, which in its
235
glTF form is a single model with 103 submeshes, mapping to a single \l Model
236
object with 103 elements in its \l{Model::materials}{materials list}, an asset
237
can contain any number of \l{Model}{models}, each with multiple submeshes and
238
associated materials. These Models can form parent-child relationships and can
239
be combined with additional \l{Node}{nodes} to perform transforms such as
240
translate, rotate, or scale. It is therefore more appropriate to look at the
241
imported asset as a complete subscene, an arbitrary tree of \l{Node}{nodes},
242
even if the rendered result is visually perceived as a single model. Open the
243
generated Sponza.qml, or any other QML file generated from such assets, in a
244
plain text editor to get an impression of the structure (which naturally always
245
depends on how the source asset, in this case the glTF file, was designed).
246
247
\section2 Importing via balsamui
248
249
For our second model, let's use the graphical user interface of \c balsam instead.
250
251
Running \c balsamui opens the tool:
252
253
\image assetintro_balsamui_startup.jpg
254
255
Let's import the
256
\l{https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Suzanne}{Suzanne}
257
model. This is a simpler model with two texture maps.
258
259
\image assetintro_balsamui_open.jpg
260
261
As there is no need for any additional configuration options, we can just
262
Convert. The result is the same as running \c balsam: a Suzanne.qml and some
263
additional files generated in the specific output directory.
264
265
\image assetintro_balsamui_convert.jpg
266
267
From this point on, working with the generated assets is the same as in the
268
previous section.
269
270
\qml
271
import QtQuick
272
import QtQuick3D
273
import QtQuick3D.Helpers
274
275
Item {
276
width: 1280
277
height: 720
278
279
View3D {
280
anchors.fill: parent
281
282
environment: SceneEnvironment {
283
backgroundMode: SceneEnvironment.Color
284
clearColor: "green"
285
}
286
287
PerspectiveCamera {
288
id: camera
289
y: 100
290
}
291
292
DirectionalLight {
293
}
294
295
Sponza {
296
scale: Qt.vector3d(100, 100, 100)
297
}
298
299
Suzanne {
300
y: 100
301
scale: Qt.vector3d(50, 50, 50)
302
eulerRotation.y: -90
303
}
304
305
WasdController {
306
controlledObject: camera
307
}
308
}
309
}
310
\endqml
311
312
Again, a scale is applied to the instantiated Suzanne node, and the Y position
313
is altered a bit so that the model does not end up in the floor of the Sponza
314
building.
315
316
\image assetintro_suzanne_first.jpg
317
318
All properties can be changed, bound to, and animated, just like with Qt Quick.
319
For example, let's apply a continuous rotation to our Suzanne model:
320
321
\qml
322
Suzanne {
323
y: 100
324
scale: Qt.vector3d(50, 50, 50)
325
NumberAnimation on eulerRotation.y {
326
from: 0
327
to: 360
328
duration: 3000
329
loops: Animation.Infinite
330
}
331
}
332
\endqml
333
334
\section1 Making it Look Better
335
336
\section2 More light
337
338
Now, our scene is a bit dark. Let's add another light. This time a \l
339
PointLight, and one that casts a shadow.
340
341
\qml
342
import QtQuick
343
import QtQuick3D
344
import QtQuick3D.Helpers
345
346
Item {
347
width: 1280
348
height: 720
349
350
View3D {
351
anchors.fill: parent
352
353
environment: SceneEnvironment {
354
backgroundMode: SceneEnvironment.Color
355
clearColor: "green"
356
}
357
358
PerspectiveCamera {
359
id: camera
360
y: 100
361
}
362
363
DirectionalLight {
364
}
365
366
Sponza {
367
scale: Qt.vector3d(100, 100, 100)
368
}
369
370
PointLight {
371
y: 200
372
color: "#d9c62b"
373
brightness: 5
374
castsShadow: true
375
shadowFactor: 75
376
}
377
378
Suzanne {
379
y: 100
380
scale: Qt.vector3d(50, 50, 50)
381
NumberAnimation on eulerRotation.y {
382
from: 0
383
to: 360
384
duration: 3000
385
loops: Animation.Infinite
386
}
387
}
388
389
WasdController {
390
controlledObject: camera
391
}
392
}
393
}
394
\endqml
395
396
Launching this scene and moving the camera around a bit reveals that this
397
is indeed starting to look better than before:
398
399
\image assetintro_suzanne_morelight.jpg
400
401
\section2 Light debugging
402
403
The \l PointLight is placed slightly above the Suzanne model. When designing
404
the scene using visual tools, such as Qt Design Studio, this is obvious, but
405
when developing without any design tools it may become handy to be able to
406
quickly visualize the location of \l{Light}{lights} and other \l{Node}{nodes}.
407
408
This we can do by adding a child node, a \l Model to the \l PointLight. The
409
position of the child node is relative to the parent, so the default \c{(0, 0,
410
0)} is effectively the position of the \l PointLight in this case. Enclosing
411
the light within some geometry (the built-in cube in this case) is not a
412
problem for the standard real-time lighting calculations since this system has
413
no concept of occlusion, meaning the light has no problems with traveling
414
through "walls". If we used \l{quick3d-lightmap}{pre-baked lightmaps}, where
415
lighting is calculated using raytracing, that would be a different story. In
416
that case we would need to make sure the cube is not blocking the light,
417
perhaps by moving our debug cube a bit above the light.
418
419
\qml
420
PointLight {
421
y: 200
422
color: "#d9c62b"
423
brightness: 5
424
castsShadow: true
425
shadowFactor: 75
426
Model {
427
source: "#Cube"
428
scale: Qt.vector3d(0.01, 0.01, 0.01)
429
materials: PrincipledMaterial {
430
lighting: PrincipledMaterial.NoLighting
431
}
432
}
433
}
434
\endqml
435
436
Another trick we use here is turning off lighting for the material used with
437
the cube. It will just appear using the default base color (white), without
438
being affected by lighting. This is handy for objects used for debugging and
439
visualizing purposes.
440
441
The result, note the small, white cube appearing, visualizing the position of
442
the \l PointLight:
443
444
\image assetintro_suzanne_cube.jpg
445
446
\section2 Skybox and image-based lighting
447
448
Another obvious improvement is doing something about the background. That green
449
clear color is not quite ideal. How about some environment that also
450
contributes to lighting?
451
452
As we do not necessarily have suitable HDRI panorama image available, let's use
453
a procedurally generated high dynamic range sky image. This is easy to do with
454
the help of \l ProceduralSkyTextureData and \l{Texture}'s support for non-file
455
based, dynamically generated image data. Instead of specifying
456
\l{Texture::source}{source}, we rather use the
457
\l{Texture::textureData}{textureData} property.
458
459
\qml
460
environment: SceneEnvironment {
461
backgroundMode: SceneEnvironment.SkyBox
462
lightProbe: Texture {
463
textureData: ProceduralSkyTextureData {
464
}
465
}
466
}
467
\endqml
468
469
\note The example code prefers defining objects inline. This is not mandatory,
470
the SceneEnvironment or ProceduralSkyTextureData objects could have also been
471
defined elsewhere in the object tree, and then referenced by \c id.
472
473
As a result, we have both a skybox and improved lighting. (the former due to
474
the \l{SceneEnvironment::backgroundMode}{backgroundMode} being set to SkyBox
475
and \l{SceneEnvironment::lightProbe}{light probe} being set to a valid \l
476
Texture; the latter due to \l{SceneEnvironment::lightProbe}{light probe} being
477
set to a valid \l Texture)
478
479
\image assetintro_sponza_ibl.jpg
480
481
\image assetintro_sponza_ibl_2.jpg
482
483
\section1 Basic Performance Investigations
484
485
To get some basic insights into the resource and performance aspects of the
486
scene, it is a good idea to add a way to show an interactive \l DebugView item
487
early on in the development process. Here we choose to add a \l Button that
488
toggles the \l DebugView, both anchored in the top-right corner.
489
490
\qml
491
import QtQuick
492
import QtQuick.Controls
493
import QtQuick3D
494
import QtQuick3D.Helpers
495
496
Item {
497
width: 1280
498
height: 720
499
500
View3D {
501
id: view3D
502
anchors.fill: parent
503
504
environment: SceneEnvironment {
505
backgroundMode: SceneEnvironment.SkyBox
506
lightProbe: Texture {
507
textureData: ProceduralSkyTextureData {
508
}
509
}
510
}
511
512
PerspectiveCamera {
513
id: camera
514
y: 100
515
}
516
517
DirectionalLight {
518
}
519
520
Sponza {
521
scale: Qt.vector3d(100, 100, 100)
522
}
523
524
PointLight {
525
y: 200
526
color: "#d9c62b"
527
brightness: 5
528
castsShadow: true
529
shadowFactor: 75
530
Model {
531
source: "#Cube"
532
scale: Qt.vector3d(0.01, 0.01, 0.01)
533
materials: PrincipledMaterial {
534
lighting: PrincipledMaterial.NoLighting
535
}
536
}
537
}
538
539
Suzanne {
540
y: 100
541
scale: Qt.vector3d(50, 50, 50)
542
NumberAnimation on eulerRotation.y {
543
from: 0
544
to: 360
545
duration: 3000
546
loops: Animation.Infinite
547
}
548
}
549
550
WasdController {
551
controlledObject: camera
552
}
553
}
554
555
Button {
556
anchors.right: parent.right
557
text: "Toggle DebugView"
558
onClicked: debugView.visible = !debugView.visible
559
DebugView {
560
id: debugView
561
source: view3D
562
visible: false
563
anchors.top: parent.bottom
564
anchors.right: parent.right
565
}
566
}
567
}
568
\endqml
569
570
\image assetintro_suzanne_debugview.jpg
571
572
This panel shows live timings, allows examining the live list of texture maps
573
and meshes, and gives an insight into the render passes that need to be
574
performed before the final color buffer can be rendered.
575
576
Due to making the \l PointLight a shadow casting light, there are multiple
577
render passes involved:
578
579
\image assetintro_suzanne_debugview_2.jpg
580
581
In the \c Textures section we see the texture maps from the Suzanne and Sponza
582
assets (the latter has a lot of them), as well as the procedurally generated
583
sky texture.
584
585
\image assetintro_suzanne_debugview_3.jpg
586
587
The \c Models page presents no surprises:
588
589
\image assetintro_suzanne_debugview_4.jpg
590
591
On the \c Tools page there are some interactive controls to toggle
592
\l{DebugSettings::wireframeEnabled}{wireframe mode} and various
593
\l{DebugSettings::materialOverride}{material overrides}.
594
595
Here with wireframe mode enabled and forcing rendering to only use the
596
\l{PrincipledMaterial::baseColor}{base color} component of the materials:
597
598
\image assetintro_suzanne_override.jpg
599
600
This concludes our tour of the basics of building a \l{Qt Quick 3D} scene with imported assets.
601
602
*/
qtquick3d
src
quick3d
doc
src
qtquick3d-assetintro.qdoc
Generated on
for Qt by
1.14.0