Qt Quick 3D Einführung in glTF-Assets
Das Beispiel „Qt Quick 3D – Einführung“ bietet eine kurze Einführung in die Erstellung von QML-basierten Anwendungen mit „ Qt Quick 3D “, verwendet dabei jedoch ausschließlich integrierte Primitive wie Kugeln und Zylinder. Diese Seite bietet eine Einführung unter Verwendung von glTF 2.0-Assets, wobei einige der Modelle aus dem Khronos glTF Sample Models-Repository zum Einsatz kommen.
Unsere Skelett-Anwendung
Beginnen wir mit der folgenden Anwendung. Dieser Codeausschnitt lässt sich unverändert mit dem Befehlszeilentool „ qml “ ausführen. Das Ergebnis ist eine komplett grüne 3D-Ansicht, in der sich sonst nichts befindet.
import QtQuick
import QtQuick3D
import QtQuick3D.Helpers
Item {
width: 1280
height: 720
View3D {
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.Color
clearColor: "green"
}
PerspectiveCamera {
id: camera
}
WasdController {
controlledObject: camera
}
}
}
Importieren eines Assets
Wir werden zwei glTF 2.0-Modelle aus dem „Sample Models“-Repository verwenden: „Sponza“ und „Suzanne“.
Diese Modelle enthalten in der Regel neben der .gltf-Datei auch eine Reihe von Textur-Maps sowie die Mesh-Daten (Geometrie), die in einer separaten Binärdatei gespeichert sind:

Wie bringen wir all das in unsere „ Qt Quick 3D “-Szene?
Es gibt verschiedene Möglichkeiten:
- QML-Komponenten generieren, die in der Szene instanziiert werden können. Das Befehlszeilentool für diese Konvertierung ist das Balsam-Tool. Neben der Erzeugung einer .qml-Datei, die im Grunde eine Unterszene darstellt, packt es auch die Mesh-Daten (Geometriedaten) in ein optimiertes, schnell ladbares Format um und kopiert zudem die Bilddateien der Textur-Maps.
- Führen Sie denselben Vorgang mit „
balsamui“ durch, einer grafischen Benutzeroberfläche für Balsam. - Bei Verwendung von Qt Design Studioist der Import von Assets in die visuellen Design-Tools integriert. Der Import kann beispielsweise durch Ziehen und Ablegen der .gltf-Datei auf das entsprechende Bedienfeld ausgelöst werden.
- Insbesondere für glTF 2.0-Assets gibt es zudem eine Laufzeitoption: den Typ „ RuntimeLoader “. Dieser ermöglicht das Laden einer .gltf-Datei (sowie der zugehörigen Binär- und Texturdatendateien) zur Laufzeit, ohne dass eine Vorverarbeitung über Tools wie Balsam erforderlich ist. Dies ist sehr praktisch in Anwendungen, die vom Benutzer bereitgestellte Assets öffnen und laden möchten. Andererseits ist dieser Ansatz hinsichtlich der Leistung deutlich weniger effizient. Daher werden wir uns in dieser Einführung nicht auf diesen Ansatz konzentrieren. Ein Beispiel für diesen Ansatz finden Sie unter „Qt Quick 3D – RuntimeLoader Example “.
Sowohl die Anwendung „ balsam “ als auch „ balsamui “ werden mit Qt ausgeliefert und sollten sich im Verzeichnis zusammen mit anderen ähnlichen ausführbaren Tools befinden, vorausgesetzt, „ Qt Quick 3D “ ist installiert oder kompiliert. In vielen Fällen reicht es aus, „balsam“ über die Befehlszeile auf die .gltf-Datei anzuwenden, ohne zusätzliche Argumente angeben zu müssen. Es lohnt sich jedoch, die zahlreichen Befehlszeilenoptionen – oder interaktive Optionen bei Verwendung von balsamui oder Qt Design Studio – zu kennen. Wenn Sie beispielsweise mit gebackenen Lightmaps arbeiten, um eine statische globale Beleuchtung zu erzielen, möchten Sie wahrscheinlich --generateLightmapUV übergeben, um den zusätzlichen Lightmap-UV-Kanal bereits beim Import der Assets generieren zu lassen, anstatt diesen potenziell ressourcenintensiven Vorgang zur Laufzeit durchzuführen. Ebenso ist „ --generateMeshLevelsOfDetail “ unerlässlich, wenn vereinfachte Versionen der Meshes generiert werden sollen, um automatisches LOD in der Szene zu ermöglichen. Andere Optionen ermöglichen das Generieren fehlender Daten (z. B. „ --generateNormals “) und das Durchführen verschiedener Optimierungen.
In „ balsamui “ werden die Befehlszeilenoptionen interaktiven Elementen zugeordnet:

Importieren über „balsam“
Los geht’s! Angenommen, das Repository „https://github.com/KhronosGroup/glTF-Sample-Models“ unter git wurde bereits an einem beliebigen Ort ausgecheckt, können wir „balsam“ einfach aus unserem Beispielanwendungsverzeichnis heraus ausführen, indem wir einen absoluten Pfad zu den .gltf-Dateien angeben:
balsam c:\work\glTF-Sample-Models\2.0\Sponza\glTF\Sponza.gltf
Dadurch erhalten wir eine Datei „ Sponza.qml “, eine Datei „ .mesh “ im Unterverzeichnis „ meshes “ sowie die Textur-Maps, die unter „ maps “ kopiert wurden.
Hinweis: Diese QML-Datei ist für sich allein nicht ausführbar. Es handelt sich um eine Komponente, die innerhalb einer 3D-Szene instanziiert werden sollte, die mit einer „ View3D “ verknüpft ist.
Unsere Projektstruktur ist hier sehr einfach, da sich die QML-Dateien der Assets direkt neben unserer Haupt-QML-Szene befinden. Dies ermöglicht es uns, den Typ „Sponza“ einfach mithilfe des Standard-QML-Komponentensystems zu instanziieren. (Zur Laufzeit wird dann im Dateisystem nach „Sponza.qml“ gesucht.)
Das bloße Hinzufügen des Modells (Subszene) ist jedoch sinnlos, da die Materialien standardmäßig die vollständigen PBR-Beleuchtungsberechnungen enthalten; daher wird von unserer Szene nichts angezeigt, solange keine Lichtquelle wie „ DirectionalLight “, „ PointLight “ oder „ SpotLight “ vorhanden ist oder die bildbasierte Beleuchtung über „ the environment “ aktiviert ist.
Vorerst fügen wir ein „ DirectionalLight “ mit den Standardeinstellungen hinzu. (Das bedeutet, die Farbe ist white und das Licht strahlt in Richtung der Z-Achse.)
import QtQuick
import QtQuick3D
import QtQuick3D.Helpers
Item {
width: 1280
height: 720
View3D {
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.Color
clearColor: "green"
}
PerspectiveCamera {
id: camera
}
DirectionalLight {
}
Sponza {
}
WasdController {
controlledObject: camera
}
}
}Wenn man dies mit dem „ qml “-Tool ausführt, wird die Szene zwar geladen und ausgeführt, ist aber standardmäßig komplett leer, da sich das Sponza-Modell hinter der Kamera befindet. Auch die Skalierung ist nicht ideal; so fühlt sich beispielsweise die Bewegung mit den WASD-Tasten und der Maus (aktiviert durch „ WasdController “) nicht richtig an.
Um dies zu beheben, skalieren wir das Sponza-Modell (Teilszene) mit „ 100 “ entlang der X-, Y- und Z-Achse. Außerdem wird die Startposition der Kamera auf der Y-Achse auf 100 erhöht.
import QtQuick
import QtQuick3D
import QtQuick3D.Helpers
Item {
width: 1280
height: 720
View3D {
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.Color
clearColor: "green"
}
PerspectiveCamera {
id: camera
y: 100
}
DirectionalLight {
}
Sponza {
scale: Qt.vector3d(100, 100, 100)
}
WasdController {
controlledObject: camera
}
}
}Wenn wir das ausführen, erhalten wir:

Mit der Maus und den WASDRF-Tasten können wir uns bewegen:


Hinweis: Wir haben oben mehrfach „ subscene “ als Alternative zu „model“ erwähnt. Warum ist das so? Während dies beim Sponza-Asset nicht offensichtlich ist – das in seiner glTF-Form ein einzelnes Modell mit 103 Submeshes ist, das einem einzelnen „ Model “-Objekt mit 103 Elementen in seiner „ materials list “ zugeordnet ist –, kann ein Asset eine beliebige Anzahl von „ models “ enthalten, von denen jedes mehrere Submeshes und zugehörige Materialien aufweist. Diese Modelle können Eltern-Kind-Beziehungen bilden und mit zusätzlichen „ nodes “ kombiniert werden, um Transformationen wie Verschieben, Drehen oder Skalieren durchzuführen. Es ist daher sinnvoller, das importierte Asset als vollständige Unterszene zu betrachten – als einen beliebigen Baum aus „ nodes “ –, auch wenn das gerenderte Ergebnis visuell als ein einziges Modell wahrgenommen wird. Öffnen Sie die generierte Datei „Sponza.qml“ oder eine beliebige andere aus solchen Assets generierte QML-Datei in einem einfachen Texteditor, um sich einen Eindruck von der Struktur zu verschaffen (die natürlich immer davon abhängt, wie das Quell-Asset, in diesem Fall die glTF-Datei, gestaltet wurde).
Import über balsamui
Für unser zweites Modell nutzen wir stattdessen die grafische Benutzeroberfläche von balsam.
Durch Aufrufen von balsamui wird das Tool geöffnet:

Importieren wir nun das Suzanne-Modell. Dabei handelt es sich um ein einfacheres Modell mit zwei Textur-Maps.

Da keine zusätzlichen Konfigurationsoptionen erforderlich sind, können wir einfach auf „Convert“ klicken. Das Ergebnis ist dasselbe wie bei der Ausführung von balsam: Eine Suzanne.qml-Datei und einige zusätzliche Dateien, die im entsprechenden Ausgabeverzeichnis generiert werden.

Ab diesem Punkt verläuft die Arbeit mit den generierten Assets genauso wie im vorherigen Abschnitt.
import QtQuick
import QtQuick3D
import QtQuick3D.Helpers
Item {
width: 1280
height: 720
View3D {
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.Color
clearColor: "green"
}
PerspectiveCamera {
id: camera
y: 100
}
DirectionalLight {
}
Sponza {
scale: Qt.vector3d(100, 100, 100)
}
Suzanne {
y: 100
scale: Qt.vector3d(50, 50, 50)
eulerRotation.y: -90
}
WasdController {
controlledObject: camera
}
}
}Auch hier wird auf den instanziierten Suzanne-Knoten eine Skalierung angewendet, und die Y-Position wird leicht angepasst, damit das Modell nicht im Boden des Sponza-Gebäudes landet.

Alle Eigenschaften können geändert, gebunden und animiert werden, genau wie bei „ Qt Quick “. Wenden wir beispielsweise eine kontinuierliche Drehung auf unser Suzanne-Modell an:
Suzanne {
y: 100
scale: Qt.vector3d(50, 50, 50)
NumberAnimation on eulerRotation.y {
from: 0
to: 360
duration: 3000
loops: Animation.Infinite
}
}Das Aussehen verbessern
Mehr Licht
Im Moment ist unsere Szene etwas dunkel. Fügen wir eine weitere Lichtquelle hinzu. Diesmal ein „ PointLight “, das Schatten wirft.
import QtQuick
import QtQuick3D
import QtQuick3D.Helpers
Item {
width: 1280
height: 720
View3D {
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.Color
clearColor: "green"
}
PerspectiveCamera {
id: camera
y: 100
}
DirectionalLight {
}
Sponza {
scale: Qt.vector3d(100, 100, 100)
}
PointLight {
y: 200
color: "#d9c62b"
brightness: 5
castsShadow: true
shadowFactor: 75
}
Suzanne {
y: 100
scale: Qt.vector3d(50, 50, 50)
NumberAnimation on eulerRotation.y {
from: 0
to: 360
duration: 3000
loops: Animation.Infinite
}
}
WasdController {
controlledObject: camera
}
}
}Wenn wir diese Szene starten und die Kamera ein wenig bewegen, sehen wir, dass es tatsächlich schon besser aussieht als zuvor:

Fehlerbehebung bei der Beleuchtung
Die „ PointLight “ ist etwas oberhalb des Suzanne-Modells platziert. Beim Entwerfen der Szene mit visuellen Werkzeugen wie „ Qt Design Studio “ ist dies offensichtlich, aber bei der Entwicklung ohne Design-Tools kann es praktisch sein, die Position von „ lights “ und anderen „ nodes “ schnell visualisieren zu können.
Dies erreichen wir, indem wir dem „ PointLight “ einen untergeordneten Knoten, einen „ Model “, hinzufügen. Die Position des untergeordneten Knotens ist relativ zum übergeordneten Knoten, sodass das standardmäßige „ (0, 0, 0) “ in diesem Fall effektiv die Position des „ PointLight “ darstellt. Das Einschließen der Lichtquelle in eine Geometrie (in diesem Fall den integrierten Würfel) stellt für die standardmäßigen Echtzeit-Beleuchtungsberechnungen kein Problem dar, da dieses System kein Konzept der Okklusion kennt – das Licht kann also problemlos durch „Wände“ hindurchdringen. Würden wir vorgefertigte Lightmaps verwenden, bei denen die Beleuchtung mittels Raytracing berechnet wird, sähe die Sache anders aus. In diesem Fall müssten wir sicherstellen, dass der Würfel das Licht nicht blockiert, beispielsweise indem wir unseren Debug-Würfel etwas über die Lichtquelle hinaus verschieben.
PointLight {
y: 200
color: "#d9c62b"
brightness: 5
castsShadow: true
shadowFactor: 75
Model {
source: "#Cube"
scale: Qt.vector3d(0.01, 0.01, 0.01)
materials: PrincipledMaterial {
lighting: PrincipledMaterial.NoLighting
}
}
}Ein weiterer Trick, den wir hier anwenden, besteht darin, die Beleuchtung für das des Würfels verwendete Material zu deaktivieren. Er erscheint dann einfach in der standardmäßigen Grundfarbe (Weiß), ohne von der Beleuchtung beeinflusst zu werden. Dies ist praktisch für Objekte, die zu Debugging- und Visualisierungszwecken verwendet werden.
Das Ergebnis: Beachten Sie den kleinen, weißen Würfel, der die Position des „ PointLight “ veranschaulicht:

Skybox und bildbasierte Beleuchtung
Eine weitere naheliegende Verbesserung besteht darin, etwas am Hintergrund zu ändern. Diese grüne, durchsichtige Farbe ist nicht ganz ideal. Wie wäre es mit einer Umgebung, die ebenfalls zur Beleuchtung beiträgt?
Da wir nicht unbedingt über ein geeignetes HDRI-Panoramabild verfügen, verwenden wir ein prozedural generiertes High-Dynamic-Range-Himmelbild. Dies lässt sich mithilfe der Unterstützung von „ ProceduralSkyTextureData “ und „ Texture “ für nicht dateibasierte, dynamisch generierte Bilddaten leicht umsetzen. Anstatt „ source “ anzugeben, verwenden wir stattdessen die Eigenschaft „ textureData “.
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.SkyBox
lightProbe: Texture {
textureData: ProceduralSkyTextureData {
}
}
}Hinweis: Im Beispielcode werden Objekte vorzugsweise inline definiert. Dies ist jedoch nicht zwingend erforderlich; die Objekte „ SceneEnvironment “ oder „ ProceduralSkyTextureData “ hätten auch an anderer Stelle im Objektbaum definiert und anschließend über „ id “ referenziert werden können.
Das Ergebnis ist sowohl eine Skybox als auch eine verbesserte Beleuchtung. (Ersteres, weil „ backgroundMode “ auf „SkyBox“ und „ light probe “ auf ein gültiges „ Texture “ gesetzt wurde; Letzteres, weil „ light probe “ auf ein gültiges „ Texture “ gesetzt wurde.)


Grundlegende Leistungsanalysen
Um grundlegende Einblicke in die Ressourcen- und Leistungsaspekte der Szene zu gewinnen, empfiehlt es sich, bereits früh im Entwicklungsprozess eine Möglichkeit zur interaktiven Anzeige eines „ DebugView “-Elements einzubauen. Hier entscheiden wir uns dafür, ein „ Button “ hinzuzufügen, das die „ DebugView “ umschaltet; beide sind in der oberen rechten Ecke verankert.
import QtQuick
import QtQuick.Controls
import QtQuick3D
import QtQuick3D.Helpers
Item {
width: 1280
height: 720
View3D {
id: view3D
anchors.fill: parent
environment: SceneEnvironment {
backgroundMode: SceneEnvironment.SkyBox
lightProbe: Texture {
textureData: ProceduralSkyTextureData {
}
}
}
PerspectiveCamera {
id: camera
y: 100
}
DirectionalLight {
}
Sponza {
scale: Qt.vector3d(100, 100, 100)
}
PointLight {
y: 200
color: "#d9c62b"
brightness: 5
castsShadow: true
shadowFactor: 75
Model {
source: "#Cube"
scale: Qt.vector3d(0.01, 0.01, 0.01)
materials: PrincipledMaterial {
lighting: PrincipledMaterial.NoLighting
}
}
}
Suzanne {
y: 100
scale: Qt.vector3d(50, 50, 50)
NumberAnimation on eulerRotation.y {
from: 0
to: 360
duration: 3000
loops: Animation.Infinite
}
}
WasdController {
controlledObject: camera
}
}
Button {
anchors.right: parent.right
text: "Toggle DebugView"
onClicked: debugView.visible = !debugView.visible
DebugView {
id: debugView
source: view3D
visible: false
anchors.top: parent.bottom
anchors.right: parent.right
}
}
}
Dieses Fenster zeigt Echtzeit-Zeitangaben an, ermöglicht die Überprüfung der Live-Liste der Textur-Maps und Meshes und gibt einen Einblick in die Render-Durchläufe, die durchgeführt werden müssen, bevor der endgültige Farbpuffer gerendert werden kann.
Da die „ PointLight “ als schattenwerfende Lichtquelle fungiert, sind mehrere Render-Durchläufe erforderlich:

Im Abschnitt „ Textures “ sehen wir die Textur-Maps der Assets „Suzanne“ und „Sponza“ (letzteres verfügt über eine Vielzahl davon) sowie die prozedural generierte Himmelstextur.

Die Seite „ Models “ hält keine Überraschungen bereit:

Auf der Seite „ Tools “ gibt es einige interaktive Steuerelemente zum Umschalten zwischen „ wireframe mode “ und verschiedenen „ material overrides “.
Hier ist der Drahtgittermodus aktiviert und das Rendering so eingestellt, dass ausschließlich die „ base color “-Komponente der Materialien verwendet wird:

Damit ist unser Überblick über die Grundlagen der Erstellung einer Qt Quick 3D Szene mit importierten Assets.
© 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.