Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
9The \l{Qt Quick 3D - Introduction} example provides a quick introduction to
10creating QML-based applications with Qt Quick 3D, but it does so using only
11built-in primitives, such as spheres and cylinders. This page provides an
12introduction using \l{https://en.wikipedia.org/wiki/GlTF#glTF_2.0}{glTF 2.0}
13assets, using some of the models from the
14\l{https://github.com/KhronosGroup/glTF-Sample-Models}{Khronos glTF Sample
15Models repository}.
16
17\section1 Our Skeleton Application
18
19Let's start with the following application. This code snippet is runnable as-is
20with the \c qml command-line tool. The result is a very green 3D view with
21nothing 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
55We are going to use two glTF 2.0 models from the Sample Models repository:
56Sponza and Suzanne.
57
58These 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
60file:
61
62\image assetintro_sponza_dir.jpg
63
64How do we get all this into our Qt Quick 3D scene?
65
66There are a number of options:
67
68\list
69
70\li Generate QML components that can be instantiated in the scene. The
71command-line tool to perform this conversion is the \l{Balsam Asset Import
72Tool}{Balsam} tool. Besides generating a .qml file, that is effectively a
73subscene, this also repacks the mesh (geometry) data into an optimized,
74fast-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
77Import Tool}{Balsam}.
78
79\li If using \l{https://www.qt.io/product/ui-design-tools}{Qt Design Studio},
80the asset import process is integrated into the visual design tools. Importing
81can be triggered, for example, by dragging and dropping the .gltf file onto the
82appropriate 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
86binary and texture data files) at runtime, without doing any pre-processing via
87tools such as \l{Balsam Asset Import Tool}{Balsam}. This is very handy in
88applications that wish to open and load user-provided assets. On the other
89hand, this approach is significantly less efficient when it comes to
90performance. Therefore, we will not be focusing on this approach in this
91introduction. Check the \l{Qt Quick 3D - RuntimeLoader Example} for an example
92of this approach.
93
94\endlist
95
96Both the \c balsam and \c balsamui applications are shipped with Qt, and should
97be present in the directory with other similar executable tools, assuming Qt
98Quick 3D is installed or built. In many cases, running \l{Balsam Asset Import
99Tool}{balsam} from the command-line on the .gltf file is sufficient, without
100having to specify any additional arguments. It is worth being aware however of
101the many command-line, or interactive if using \c balsamui or Qt Design Studio,
102options. For example, when working with \l{quick3d-lightmap}{baked lightmaps to
103provide static global illumination}, it is likely that one will want to pass
104\c{--generateLightmapUV} to get the additional lightmap UV channel generated at
105asset import time, instead of performing this potentially consuming process at
106run-time. Similarly, \c{--generateMeshLevelsOfDetail} is essential when it is
107desirable 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
109allow generating missing data (e.g. \c{--generateNormals}) and performing various
110optimizations.
111
112In \c balsamui the command-line options are mapped to interactive elements:
113\image assetintro_balsamui_options.jpg
114
115\section2 Importing via balsam
116
117Let's get started! Assuming that the
118\l{https://github.com/KhronosGroup/glTF-Sample-Models} \c git repository is
119checked out somewhere, we can simply run balsam from our example application
120directory, 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
124This gives us a \c{Sponza.qml}, a \c{.mesh} file under the \c meshes
125subdirectory, 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
128should be instantiated within a 3D scene associated with a \l View3D.
129
130Our project structure is very simple here, as the asset qml files live right
131next to our main .qml scene. This allows us to simply instantiate the Sponza
132type using the \l{QML Documents}{standard QML component system}. (at run-time
133this will then look for Sponza.qml in the filesystem)
134
135Just adding the model (subscene) is pointless however, since by default the
136materials feature the full \l{quick3d-pbr}{PBR lighting calculations}, so
137nothing is shown from our scene without a light such as \l DirectionalLight, \l
138PointLight, or \l SpotLight, or having \l{Using Image-Based
139Lighting}{image-based lighting} enabled via
140\l{SceneEnvironment::lightProbe}{the environment}.
141
142For now, we choose to add a DirectionalLight with the default settings. (meaning
143the 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
179Running this with the \c qml tool will load and run, but the scene is all empty
180by default since the Sponza model is behind the camera. The scale is also not
181ideal, e.g. moving around with WASD keys and the mouse (enabled by the
182\l WasdController) does not feel right.
183
184To remedy this, we scale the Sponza model (subscene) by \c 100 along the X, Y,
185and 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
223Running this gives us:
224
225\image assetintro_sponza_first.jpg
226
227With 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
235glTF form is a single model with 103 submeshes, mapping to a single \l Model
236object with 103 elements in its \l{Model::materials}{materials list}, an asset
237can contain any number of \l{Model}{models}, each with multiple submeshes and
238associated materials. These Models can form parent-child relationships and can
239be combined with additional \l{Node}{nodes} to perform transforms such as
240translate, rotate, or scale. It is therefore more appropriate to look at the
241imported asset as a complete subscene, an arbitrary tree of \l{Node}{nodes},
242even if the rendered result is visually perceived as a single model. Open the
243generated Sponza.qml, or any other QML file generated from such assets, in a
244plain text editor to get an impression of the structure (which naturally always
245depends on how the source asset, in this case the glTF file, was designed).
246
247\section2 Importing via balsamui
248
249For our second model, let's use the graphical user interface of \c balsam instead.
250
251Running \c balsamui opens the tool:
252
253\image assetintro_balsamui_startup.jpg
254
255Let's import the
256\l{https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Suzanne}{Suzanne}
257model. This is a simpler model with two texture maps.
258
259\image assetintro_balsamui_open.jpg
260
261As there is no need for any additional configuration options, we can just
262Convert. The result is the same as running \c balsam: a Suzanne.qml and some
263additional files generated in the specific output directory.
264
265\image assetintro_balsamui_convert.jpg
266
267From this point on, working with the generated assets is the same as in the
268previous 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
312Again, a scale is applied to the instantiated Suzanne node, and the Y position
313is altered a bit so that the model does not end up in the floor of the Sponza
314building.
315
316\image assetintro_suzanne_first.jpg
317
318All properties can be changed, bound to, and animated, just like with Qt Quick.
319For 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
338Now, our scene is a bit dark. Let's add another light. This time a \l
339PointLight, 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
396Launching this scene and moving the camera around a bit reveals that this
397is indeed starting to look better than before:
398
399\image assetintro_suzanne_morelight.jpg
400
401\section2 Light debugging
402
403The \l PointLight is placed slightly above the Suzanne model. When designing
404the scene using visual tools, such as Qt Design Studio, this is obvious, but
405when developing without any design tools it may become handy to be able to
406quickly visualize the location of \l{Light}{lights} and other \l{Node}{nodes}.
407
408This we can do by adding a child node, a \l Model to the \l PointLight. The
409position of the child node is relative to the parent, so the default \c{(0, 0,
4100)} is effectively the position of the \l PointLight in this case. Enclosing
411the light within some geometry (the built-in cube in this case) is not a
412problem for the standard real-time lighting calculations since this system has
413no concept of occlusion, meaning the light has no problems with traveling
414through "walls". If we used \l{quick3d-lightmap}{pre-baked lightmaps}, where
415lighting is calculated using raytracing, that would be a different story. In
416that case we would need to make sure the cube is not blocking the light,
417perhaps 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
436Another trick we use here is turning off lighting for the material used with
437the cube. It will just appear using the default base color (white), without
438being affected by lighting. This is handy for objects used for debugging and
439visualizing purposes.
440
441The result, note the small, white cube appearing, visualizing the position of
442the \l PointLight:
443
444\image assetintro_suzanne_cube.jpg
445
446\section2 Skybox and image-based lighting
447
448Another obvious improvement is doing something about the background. That green
449clear color is not quite ideal. How about some environment that also
450contributes to lighting?
451
452As we do not necessarily have suitable HDRI panorama image available, let's use
453a procedurally generated high dynamic range sky image. This is easy to do with
454the help of \l ProceduralSkyTextureData and \l{Texture}'s support for non-file
455based, 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,
470the SceneEnvironment or ProceduralSkyTextureData objects could have also been
471defined elsewhere in the object tree, and then referenced by \c id.
472
473As a result, we have both a skybox and improved lighting. (the former due to
474the \l{SceneEnvironment::backgroundMode}{backgroundMode} being set to SkyBox
475and \l{SceneEnvironment::lightProbe}{light probe} being set to a valid \l
476Texture; the latter due to \l{SceneEnvironment::lightProbe}{light probe} being
477set 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
485To get some basic insights into the resource and performance aspects of the
486scene, it is a good idea to add a way to show an interactive \l DebugView item
487early on in the development process. Here we choose to add a \l Button that
488toggles 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
572This panel shows live timings, allows examining the live list of texture maps
573and meshes, and gives an insight into the render passes that need to be
574performed before the final color buffer can be rendered.
575
576Due to making the \l PointLight a shadow casting light, there are multiple
577render passes involved:
578
579\image assetintro_suzanne_debugview_2.jpg
580
581In the \c Textures section we see the texture maps from the Suzanne and Sponza
582assets (the latter has a lot of them), as well as the procedurally generated
583sky texture.
584
585\image assetintro_suzanne_debugview_3.jpg
586
587The \c Models page presents no surprises:
588
589\image assetintro_suzanne_debugview_4.jpg
590
591On 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
595Here 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
600This concludes our tour of the basics of building a \l{Qt Quick 3D} scene with imported assets.
601
602*/