Qt 3D Überblick
Qt 3D bietet einen vollständig konfigurierbaren Renderer, mit dem Entwickler schnell jede benötigte Rendering-Pipeline implementieren können. Darüber hinaus bietet Qt 3D ein generisches Framework für echtzeitnahe Simulationen über das Rendering hinaus.
Qt 3D ist sauber in einen Kern und eine beliebige Anzahl von Aspekten aufgeteilt, die jede gewünschte Funktionalität implementieren können. Die Aspekte interagieren mit Komponenten und Entitäten, um einen Teil der Funktionalität bereitzustellen. Beispiele für Aspekte sind Physik, Audio, Kollision, künstliche Intelligenz (AI) und Pfadfindung.
Grundlegende 3D-Funktionen
Qt 3D ist ein 3D-Framework, das das Zeichnen von 3D-Formen und deren Bewegung sowie das Bewegen der Kamera ermöglicht. Es unterstützt die folgenden grundlegenden Funktionen:
- 2D und 3D rendering für C++ und Qt Quick Anwendungen
- Meshes und Geometrie
- Materials
- Schattierer
- Schatten-Mapping
- Ambient occlusion
- Hoher Dynamikbereich
- Aufgeschobenes Rendering
- Multitexturierung
- Instanzielles Rendering
- Einheitliche Puffer-Objekte
- Portierung auf RHI
- Profi-Tipps
Materialien
Qt 3D verfügt über ein robustes und sehr flexibles Materialsystem, das mehrere Ebenen der Anpassung ermöglicht. Es eignet sich für unterschiedliche Rendering-Ansätze auf verschiedenen Plattformen oder OpenGL-Versionen, ermöglicht mehrere Rendering-Durchläufe mit unterschiedlichen Status-Sets, bietet Mechanismen für das Überschreiben von Parametern auf verschiedenen Ebenen und ermöglicht einen einfachen Wechsel von Shadern. All dies kann von C++ aus oder über QML-Eigenschaftsbindungen erfolgen.
Die Eigenschaften eines Material -Typs können leicht auf einheitliche Variablen in einem GLSL-Shader-Programm abgebildet werden, das selbst in der referenzierten Effekteigenschaft angegeben ist.
Beispiele für die Verwendung von Materialien finden Sie in den folgenden Beispielen:
Shader
Qt 3D unterstützt alle Stufen der programmierbaren OpenGL-Rendering-Pipeline: Vertex-, Tessellation Control-, Tessellation Evaluation-, Geometrie- und Fragment-Shader. Compute-Shader sind für eine zukünftige Version geplant.
Beispiele für die Verwendung von Shadern finden Sie im Qt 3D: Wireframe QML Example.
Schatten-Mapping
Schatten werden von OpenGL nicht direkt unterstützt, aber es gibt zahllose Techniken, mit denen sie erzeugt werden können. Shadow Mapping ist einfach zu verwenden, um gut aussehende Schatten zu erzeugen, während es nur sehr geringe Leistungskosten verursacht.
Die Schattenzuordnung wird in der Regel mit einem Rendering in zwei Durchgängen implementiert. Im ersten Durchgang werden die Schatteninformationen erzeugt. Im zweiten Durchgang wird die Szene mit einer bestimmten Rendering-Technik generiert, während gleichzeitig die im ersten Durchgang gesammelten Informationen zum Zeichnen der Schatten verwendet werden.
Die Idee hinter dem Shadow Mapping ist, dass nur die Fragmente, die dem Licht am nächsten sind, beleuchtet werden. Fragmente hinter anderen Fragmenten werden verdeckt und liegen daher im Schatten.
Daher wird die Szene im ersten Durchgang aus dem Blickwinkel des Lichts gezeichnet. Die Information, die gespeichert wird, ist einfach der Abstand des nächstgelegenen Fragments in diesem Lichtraum. In OpenGL-Begriffen entspricht dies einem Framebuffer-Objekt (FBO), dem eine Tiefentextur zugeordnet ist. Tatsächlich ist der Abstand zum Auge die Definition der Tiefe, und die standardmäßige Tiefenprüfung von OpenGL speichert tatsächlich nur die Tiefe für das nächstgelegene Fragment.
Eine angehängte Farbtextur wird nicht einmal benötigt, da es nicht notwendig ist, Fragmente zu schattieren, sondern nur ihre Tiefe zu berechnen.
Das folgende Bild zeigt eine Szene mit einer selbstbeschatteten Ebene und einem Kleeblattknoten:
Das folgende Bild zeigt eine übertriebene Shadow-Map-Textur der Szene:
Das Bild zeigt die Tiefe, die beim Rendern der Szene aus der Lichtperspektive gespeichert wird. Dunklere Farben stehen für eine geringere Tiefe (d. h. näher an der Kamera). In dieser Szene ist das Licht irgendwo oberhalb der Objekte in der Szene platziert, auf der rechten Seite in Bezug auf die Hauptkamera (vergleichen Sie dies mit dem ersten Screenshot). Dies stimmt mit der Tatsache überein, dass die Spielzeugebene näher an der Kamera ist als die anderen Objekte.
Sobald die Schattenkarte erstellt ist, wird der zweite Rendering-Durchgang durchgeführt. In diesem zweiten Durchgang wird das Rendering mit der Kamera der normalen Szene durchgeführt. Hier kann jeder beliebige Effekt verwendet werden, z. B. Phong-Shading. Wichtig ist, dass der Schattenkartenalgorithmus im Fragment-Shader angewendet wird. Das heißt, dass das Fragment, das dem Licht am nächsten ist, beleuchtet gezeichnet wird, während die anderen Fragmente im Schatten gezeichnet werden.
Die im ersten Durchgang erzeugte Schattenkarte liefert die notwendigen Informationen über den Abstand der Fragmente zum Licht. Es reicht dann aus, das Fragment im Lichtraum neu abzubilden und dabei seine Tiefe vom Standpunkt des Lichts aus zu berechnen sowie seine Koordinaten auf der Shadow-Map-Textur zu bestimmen. Die Shadow-Map-Textur kann dann an den gegebenen Koordinaten abgetastet werden und die Tiefe des Fragments kann mit dem Ergebnis der Abtastung verglichen werden. Wenn das Fragment weiter entfernt ist, befindet es sich im Schatten, andernfalls ist es beleuchtet.
Instanzielles Rendering
Instanzierung ist eine Möglichkeit, die GPU dazu zu bringen, viele Kopien (Instanzen) eines Basisobjekts zu zeichnen, die sich bei jeder Kopie in irgendeiner Weise unterscheiden. Häufig in Position, Ausrichtung, Farbe, Materialeigenschaften, Skalierung usw. Qt 3D bietet eine API, die dem Qt Quick Repeater Element ähnelt. In diesem Fall ist der Delegat das Basisobjekt und das Modell liefert die Daten pro Instanz. Während also eine Entität mit einer Mesh Komponente letztendlich in einen Aufruf von glDrawElements umgewandelt wird, wird eine Entität mit einer instanzierten Komponente in einen Aufruf von glDrawElementsInstanced übersetzt.
Instanced Rendering ist für eine zukünftige Version geplant.
Einheitliche Puffer-Objekte
Ein Uniform Buffer Object (UBO) kann an OpenGL-Shader-Programme gebunden werden, um große Datenmengen leicht verfügbar zu machen. Typische Anwendungsfälle für UBOs sind Sätze von Material- oder Beleuchtungsparametern.
Nützliche Tipps
Einige sehr nützliche Programmiertipps für 3D-Rendering finden Sie auf dieser Seite: Qt 3D Render Pro Tipps.
Konfigurierbarer Renderer
Um die Unterstützung für C++- und QML-APIs mit einem vollständig konfigurierbaren Renderer zu kombinieren, wurde das Konzept eines Framegraphen eingeführt. Während ein Scenegraph eine datengesteuerte Beschreibung dessen ist , was gerendert werden soll, ist ein Framegraph eine datengesteuerte Beschreibung dessen, wie es gerendert werden soll.
Ein Framegraph ermöglicht es den Entwicklern, zwischen einem einfachen Vorwärts-Renderer, einschließlich eines Z-Fill-Passes, oder einem Deferred Renderer zu wählen. Es gibt ihnen auch die Kontrolle darüber, wann transparente Objekte gerendert werden sollen usw. Da dies alles rein datenbasiert konfiguriert wird, ist es sehr einfach, auch dynamisch zur Laufzeit Änderungen vorzunehmen, ohne dass C++-Code berührt werden muss. Es ist möglich, Qt 3D zu erweitern, indem Sie Ihre eigenen Framegraphen erstellen, die benutzerdefinierte Rendering-Algorithmen implementieren.
3D-Erweiterungen
Über das Wesentliche der Darstellung von 3D-Inhalten auf dem Bildschirm hinaus ist Qt 3D erweiterbar und flexibel genug, um als Host für die folgenden Arten von Erweiterungen im Zusammenhang mit 3D-Objekten zu dienen:
- Physiksimulation
- Kollisionserkennung
- 3D-Audio zur Positionsbestimmung
- Starrkörper-, Skelett- und Morph-Ziel-Animation
- Pfadfindung und andere AI
- Auswählen
- Partikel
- Objekt-Spawning
Leistung
Qt 3D ist so konzipiert, dass die Leistung gut ist und mit der Anzahl der verfügbaren CPU-Kerne steigt, da moderne Hardware die Leistung eher durch die Erhöhung der Anzahl der Kerne als durch die Erhöhung der Basistaktrate verbessert. Die Verwendung mehrerer Kerne funktioniert gut, da viele Aufgaben voneinander unabhängig sind. So überschneiden sich beispielsweise die von einem Modul zur Pfadsuche durchgeführten Operationen nicht stark mit den Aufgaben eines Renderers, außer vielleicht beim Rendern von Debug-Informationen oder Statistiken.
Qt 3D Architektur
Die Hauptanwendungsfälle von Qt 3D sind die Simulation von Objekten in Beinahe-Echtzeit und das Rendern des Zustands dieser Objekte auf dem Bildschirm. Das Space Invaders-Beispiel enthält die folgenden Objekte:
- Die Bodenkanone des Spielers
- Der Boden
- Die Verteidigungsblöcke
- Die feindlichen Raumschiffe der Invasoren
- Die feindliche fliegende Untertasse des Endgegners
- Die von den Feinden und dem Spieler verschossenen Kugeln
In einem traditionellen C++-Design würden diese Objekttypen normalerweise als Klassen implementiert, die in einer Art Vererbungsbaum angeordnet sind. Verschiedene Verzweigungen im Vererbungsbaum könnten der Stammklasse zusätzliche Funktionen hinzufügen, z. B:
- Akzeptiert Benutzereingaben
- Spielt einen Ton ab
- ist animiert
- Kollidiert mit anderen Objekten
- Wird auf dem Bildschirm gezeichnet
Die Typen im Space Invaders-Beispiel können anhand dieser Merkmale klassifiziert werden. Es ist jedoch nicht einfach, einen eleganten Vererbungsbaum selbst für ein so einfaches Beispiel zu entwerfen.
Dieser Ansatz und andere Varianten der Vererbung werfen eine Reihe von Problemen auf:
- Tiefe und breite Vererbungshierarchien sind schwer zu verstehen, zu pflegen und zu erweitern.
- Die Vererbungstaxonomie ist zur Kompilierzeit in Stein gemeißelt.
- Jede Ebene im Klassenvererbungsbaum kann nur nach einem einzigen Kriterium oder einer Achse klassifiziert werden.
- Gemeinsame Funktionalität tendiert dazu, mit der Zeit in der Klassenhierarchie nach oben zu klettern.
- Es ist unmöglich, vorherzusagen, was die Entwickler tun wollen.
Die Erweiterung tiefer und breiter Vererbungsbäume erfordert in der Regel das Verständnis und die Übereinstimmung mit der vom ursprünglichen Autor verwendeten Taxonomie. Daher legt Qt 3D den Schwerpunkt auf die Aggregation statt auf die Vererbung als Mittel zur Übertragung von Funktionalität auf eine Instanz eines Objekts. Konkret implementiert Qt 3D ein Entity Component System (ECS).
Verwendung eines ECS
In einem ECS stellt eine Entität ein simuliertes Objekt dar, das jedoch selbst keine spezifischen Verhaltensweisen oder Eigenschaften aufweist. Zusätzliches Verhalten kann auf eine Entität aufgepfropft werden, indem die Entität eine oder mehrere Komponenten zusammenfasst. Jede Komponente ist ein vertikaler Ausschnitt des Verhaltens eines Objekttyps.
Im Beispiel der Space Invaders ist der Boden eine Entität mit einer angehängten Komponente, die dem System mitteilt, dass die Entität gerendert werden muss und welche Art von Rendering sie benötigt. Ein feindliches Raumschiff ist eine weitere Entität mit angehängten Komponenten, die dafür sorgen, dass das Schiff gerendert wird, aber auch dafür, dass es Geräusche von sich gibt, dass es mit ihm kollidiert, dass es animiert wird und dass es von einer einfachen KI gesteuert wird.
Die Bodenkanone des Spielers hat größtenteils ähnliche Komponenten wie das feindliche Raumschiff, außer dass sie keine KI-Komponente hat. Stattdessen verfügt die Kanone über eine Eingabekomponente, die es dem Spieler ermöglicht, sie zu bewegen und Geschosse abzufeuern.
ECS-Backend
Das Backend von Qt 3D implementiert den Systemteil des ECS-Paradigmas in Form von Aspekten. Ein Aspekt implementiert einen bestimmten vertikalen Ausschnitt der Funktionalität, die Entitäten durch eine Kombination aus einer oder mehreren ihrer aggregierten Komponenten zur Verfügung gestellt wird.
Der Renderer-Aspekt sucht zum Beispiel nach Objekten, die Netz-, Material- und optional Transformationskomponenten haben. Wenn der Renderer-Aspekt ein solches Objekt findet, weiß er, wie er diese Daten nehmen und etwas Schönes daraus zeichnen kann. Wenn eine Entität diese Komponenten nicht hat, wird sie vom Renderer-Aspekt ignoriert.
Qt 3D erstellt benutzerdefinierte Entitäten durch Aggregation von Komponenten, die zusätzliche Fähigkeiten bieten. Die Qt 3D Engine verwendet Aspekte, um Entities mit bestimmten Komponenten zu verarbeiten und zu aktualisieren.
Zum Beispiel sucht ein Physik-Aspekt nach Entities, die eine Art von Kollisionsvolumen-Komponente und eine andere Komponente haben, die andere Eigenschaften angibt, die für solche Simulationen benötigt werden, wie Masse, Reibungskoeffizient usw. Eine Entität, die Geräusche abgibt, hat eine Komponente, die angibt, dass es sich um einen Geräuschemitter handelt, und die auch angibt, wann und welche Geräusche abgespielt werden.
Da ECS eher auf Aggregation als auf Vererbung setzt, ist es möglich, das Verhalten eines Objekts zur Laufzeit dynamisch zu ändern, indem einfach Komponenten hinzugefügt oder entfernt werden.
Um zum Beispiel einem Spieler zu ermöglichen, nach dem Einschalten plötzlich durch Wände zu rennen, kann die Komponente für die Kollisionslautstärke dieses Objekts vorübergehend entfernt werden, bis die Einschaltzeit abgelaufen ist. Es besteht keine Notwendigkeit, eine spezielle, einmalige Unterklasse für PlayerWhoRunsThroughWalls
zu erstellen.
Qt 3D ECS-Implementierung
Qt 3D implementiert ECS als eine einfache Klassenhierarchie. Die Basisklasse Qt 3D ist Qt3DCore::QNode, die eine Unterklasse von QObject ist. Qt3DCore::QNode fügt QObject die Fähigkeit hinzu, automatisch Eigenschaftsänderungen an Aspekte und eine ID zu übermitteln, die in einer Anwendung eindeutig ist. Die Aspekte existieren in zusätzlichen Threads und Qt3DCore::QNode vereinfacht die Datenübertragung zwischen den benutzerorientierten Objekten und den Aspekten.
Typischerweise stellen Unterklassen von Qt3DCore::QNode zusätzliche unterstützende Daten zur Verfügung, die von Komponenten referenziert werden. Die Klasse QShaderProgram gibt beispielsweise den GLSL-Code an, der beim Rendern einer Reihe von Objekten verwendet werden soll.
Die Komponenten in Qt 3D werden durch Unterklassifizierung von Qt3DCore::QComponent und Hinzufügung der Daten implementiert, die für die Arbeit des entsprechenden Aspekts erforderlich sind. Zum Beispiel wird die Mesh-Komponente vom Renderer-Aspekt verwendet, um die Daten pro Vertex abzurufen, die an die OpenGL-Pipeline gesendet werden sollen.
Schließlich ist Qt3DCore::QEntity einfach ein Objekt, das null oder mehr Instanzen von Qt3DCore::QComponent zusammenfassen kann.
Erweitern von Qt 3D
Das Hinzufügen von Funktionalität zu Qt 3D, entweder als Teil von Qt oder spezifisch für Ihre eigenen Anwendungen, um von dem Multi-Threaded Back-End zu profitieren, besteht aus den folgenden Aufgaben:
- Identifizieren und implementieren Sie alle notwendigen Komponenten und unterstützenden Daten.
- Registrieren Sie die Komponenten bei der QML-Engine (nur wenn Sie die QML-API verwenden).
- Unterklassifizieren Sie QAbstractAspect und implementieren Sie die Funktionalität des Subsystems.
Qt 3D Aufgabenbasierter Motor
In Qt 3D werden Aspekte in jedem Frame nach einer Reihe von Aufgaben gefragt, die zusammen mit den Abhängigkeiten zwischen ihnen ausgeführt werden sollen. Die Aufgaben werden von einem Scheduler auf alle konfigurierten Kerne verteilt, um die Leistung zu verbessern.
Qt 3DAspekte
Standardmäßig bietet Qt 3D die Aspekte Qt3DRender und Qt3DInput. Die Komponenten und andere unterstützende Klassen, die von diesen Aspekten bereitgestellt werden, werden in der Dokumentation für diese Module beschrieben.
Zusätzliche Aspekte, die mehr Möglichkeiten bieten, werden in zukünftigen Versionen von Qt 3D hinzugefügt.
© 2025 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.