Qt 3D Framegraph de rendu
L'aspect « Rendu » d'Qt 3D permet à l'algorithme de rendu d'être entièrement piloté par les données. La structure de données qui le contrôle est appelée « framegraph ». À l’instar du système ECS (Entity Component System) d’ Qt 3D, qui vous permet de définir ce qu’on appelle un « Scenegraph » en construisant une scène à partir d’une arborescence d’entités et de composants, le « framegraph » est également une structure arborescente, mais utilisée à des fins différentes. Il sert notamment à contrôler la manière dont la scène est rendue.
Au cours du rendu d’une seule image, un moteur de rendu 3D changera probablement d’état à de nombreuses reprises. Le nombre et la nature de ces changements d’état dépendent non seulement des matériaux (shaders, géométrie des maillages, textures et variables uniformes) présents dans la scène, mais aussi du schéma de rendu de haut niveau que vous utilisez.
Par exemple, l’utilisation d’un schéma de rendu direct simple et traditionnel est très différente de celle d’une approche de rendu différé. D’autres fonctionnalités telles que les reflets, les ombres, les fenêtres d’affichage multiples et les passes de remplissage Z anticipées modifient toutes les états qu’un moteur de rendu doit définir au cours d’une image, ainsi que le moment où ces changements d’état doivent se produire.
À titre de comparaison, le moteur de rendu de graphe de scène d’Qt Quick 2, chargé de dessiner les scènes d’ Qt Quick 2, est codé en dur en C++ pour effectuer des opérations telles que le regroupement de primitives et le rendu des éléments opaques suivi de celui des éléments transparents. Dans le cas d’ Qt Quick 2, cela convient parfaitement, car cela couvre l’ensemble des exigences. Comme vous pouvez le constater à partir de certains des exemples cités ci-dessus, un tel moteur de rendu codé en dur n’est probablement pas assez flexible pour des scènes 3D génériques, compte tenu de la multitude de méthodes de rendu disponibles. Ou bien, si un moteur de rendu pouvait être rendu suffisamment flexible pour couvrir tous ces cas, ses performances risqueraient d’en pâtir en raison de son caractère trop général. Pour ne rien arranger, de nouvelles méthodes de rendu font sans cesse l’objet de recherches. Nous avions donc besoin d’une approche à la fois flexible et extensible, tout en restant simple à utiliser et à maintenir. C’est là qu’intervient le framegraph !
Chaque nœud du framegraph définit une partie de la configuration que le moteur de rendu utilisera pour rendre la scène. La position d’un nœud dans l’arborescence du framegraph détermine quand et où la sous-arborescence dont ce nœud est la racine deviendra la configuration active dans le pipeline de rendu. Comme nous le verrons plus loin, le moteur de rendu parcourt cette arborescence afin de construire l’état nécessaire à votre algorithme de rendu à chaque instant de l’image.
Évidemment, si vous souhaitez simplement afficher un simple cube à l'écran, vous pourriez penser que c’est excessif. Cependant, dès que vous souhaitez créer des scènes un peu plus complexes, cela s’avère très utile. Pour les cas courants, Qt 3D fournit quelques exemples de framegraphs prêts à l’emploi.
Nous allons illustrer la flexibilité du concept de framegraph en présentant quelques exemples et les framegraphs qui en résultent.
Veuillez noter que, contrairement au Scenegraph qui est composé d’entités et de composants, le framegraph est uniquement composé de nœuds imbriqués qui sont tous des sous-classes de Qt3DRender::QFrameGraphNode. En effet, les nœuds du framegraph ne sont pas des objets simulés dans notre monde virtuel, mais plutôt des informations de support.
Nous verrons bientôt comment construire notre premier framegraph simple, mais avant cela, nous allons vous présenter les nœuds de framegraph à votre disposition. De même que pour l’arborescence du Scenegraph, les API QML et C++ correspondent parfaitement l’une à l’autre ; vous pouvez donc privilégier celle qui vous convient le mieux. Par souci de lisibilité et de concision, l’API QML a été choisie pour cet article.
L’intérêt du framegraph réside dans le fait qu’en combinant ces types de nœuds simples, il est possible de configurer le moteur de rendu pour qu’il réponde à vos besoins spécifiques sans avoir à toucher au moindre code de rendu C/C++ complexe et de bas niveau.
Règles du FrameGraph
Afin de construire un arbre framegraph fonctionnant correctement, vous devez connaître quelques règles concernant la manière dont il est parcouru et comment le transmettre au moteur de rendu Qt 3D.
Configuration du FrameGraph
L’arborescence FrameGraph doit être attribuée à la propriété `activeFrameGraph` d’un composant `QRenderSettings`, lui-même composant de l’entité racine de la scène d’ Qt 3D. C’est ce qui en fait le framegraph actif pour le moteur de rendu. Bien sûr, comme il s’agit d’une liaison de propriété QML, le framegraph actif (ou certaines de ses parties) peut être modifié à la volée lors de l’exécution. Par exemple, si vous souhaitez utiliser des approches de rendu différentes pour les scènes d’intérieur et d’extérieur, ou pour activer ou désactiver certains effets spéciaux.
Entity {
id: sceneRoot
components: RenderSettings {
activeFrameGraph: ... // FrameGraph tree
}
}Remarque : `activeFrameGraph ` est la propriété par défaut du composant `FrameGraph` en QML.
Entity {
id: sceneRoot
components: RenderSettings {
... // FrameGraph tree
}
}Utilisation du framegraph
- Le moteur de rendu « Qt 3D » effectue une traversée en profondeur de l’arborescence du framegraph. Notez que, la traversée se faisant en profondeur, l’ordre dans lequel vous définissez les nœuds est important.
- Lorsque le moteur de rendu atteint un nœud feuille du framegraph, il rassemble tous les états spécifiés par le chemin allant du nœud feuille au nœud racine. Cela définit l’état utilisé pour rendre une section de l’image. Si le fonctionnement interne d’ Qt 3D vous intéresse, sachez que cet ensemble d’états est appelé RenderView.
- À partir de la configuration contenue dans une RenderView, le moteur de rendu rassemble toutes les entités du graphe de scène à rendre, puis construit à partir de celles-ci un ensemble de RenderCommands qu’il associe à la RenderView.
- La combinaison de la RenderView et de l’ensemble de RenderCommands est transmise à OpenGL pour exécution.
- Lorsque cette opération est répétée pour chaque nœud feuille du framegraph, l’image est complète et le moteur de rendu appelle la fonction ` QOpenGLContext::swapBuffers()` pour l’afficher.
Fondamentalement, le framegraph est une méthode orientée données permettant de configurer le moteur de rendu Qt 3D. Grâce à sa nature orientée données, il est possible de modifier la configuration à l'exécution, de permettre à des développeurs ou concepteurs non spécialisés en C++ de modifier la structure d'une image, et d'expérimenter de nouvelles approches de rendu sans avoir à écrire des milliers de lignes de code standard.
Exemples de framegraph
Maintenant que vous connaissez les règles à respecter lors de l’écriture d’une arborescence de framegraph, nous allons passer en revue quelques exemples et les analyser en détail.
Un moteur de rendu direct simple
Le rendu direct consiste à utiliser OpenGL de manière traditionnelle et à effectuer le rendu directement dans le backbuffer, un objet à la fois, en appliquant le shading à chacun d’entre eux au fur et à mesure. Cela s’oppose au rendu différé, où l’on effectue le rendu dans un G-buffer intermédiaire. Voici un FrameGraph simple pouvant être utilisé pour le rendu direct :
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
property alias camera: cameraSelector.camera
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
CameraSelector {
id: cameraSelector
}
}
}Comme vous pouvez le constater, cet arbre comporte une seule feuille et est composé de 3 nœuds au total, comme le montre le schéma suivant.

En appliquant les règles définies ci-dessus, cet arbre FrameGraph génère un seul RenderView avec la configuration suivante :
- Nœud feuille -> RenderView
- Fenêtre d’affichage occupant tout l’écran (utilise des coordonnées normalisées pour faciliter la prise en charge des fenêtres d’affichage imbriquées)
- Les tampons de couleur et de profondeur sont configurés pour être effacés
- Caméra spécifiée dans la propriété « camera » exposée
Plusieurs arborescences FrameGraph différentes peuvent produire le même résultat de rendu. Tant que l’état collecté de la feuille à la racine est identique, le résultat sera lui aussi identique. Il est préférable de placer l’état qui reste constant le plus longtemps près de la racine de l’arborescence FrameGraph, car cela réduira le nombre de nœuds feuilles et, par conséquent, le nombre total de RenderViews.
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
property alias camera: cameraSelector.camera
CameraSelector {
id: cameraSelector
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
}
}
}CameraSelector {
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
}
}
}Un FrameGraph à vues multiples
Passons maintenant à un exemple légèrement plus complexe qui effectue le rendu d’un Scenegraph du point de vue de 4 caméras virtuelles dans les 4 quadrants de la fenêtre. Il s’agit d’une configuration courante pour les outils de CAO 3D ou de modélisation ; elle pourrait également être adaptée pour faciliter le rendu d’un rétroviseur dans un jeu de course automobile ou l’affichage d’une caméra de vidéosurveillance.

Viewport {
id: mainViewport
normalizedRect: Qt.rect(0, 0, 1, 1)
property alias Camera: cameraSelectorTopLeftViewport.camera
property alias Camera: cameraSelectorTopRightViewport.camera
property alias Camera: cameraSelectorBottomLeftViewport.camera
property alias Camera: cameraSelectorBottomRightViewport.camera
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
}
Viewport {
id: topLeftViewport
normalizedRect: Qt.rect(0, 0, 0.5, 0.5)
CameraSelector { id: cameraSelectorTopLeftViewport }
}
Viewport {
id: topRightViewport
normalizedRect: Qt.rect(0.5, 0, 0.5, 0.5)
CameraSelector { id: cameraSelectorTopRightViewport }
}
Viewport {
id: bottomLeftViewport
normalizedRect: Qt.rect(0, 0.5, 0.5, 0.5)
CameraSelector { id: cameraSelectorBottomLeftViewport }
}
Viewport {
id: bottomRightViewport
normalizedRect: Qt.rect(0.5, 0.5, 0.5, 0.5)
CameraSelector { id: cameraSelectorBottomRightViewport }
}
}Cet arbre est un peu plus complexe, avec cinq feuilles. En suivant les mêmes règles que précédemment, nous construisons cinq objets RenderView à partir du FrameGraph. Les schémas suivants illustrent la construction des deux premières RenderViews. Les RenderViews restantes sont très similaires à celles du deuxième schéma, mais avec les autres sous-arbres.


Au total, les RenderViews créés sont les suivants :
- RenderView (1)
- Fenêtre d’affichage plein écran définie
- Les tampons de couleur et de profondeur sont configurés pour être effacés
- RenderView (2)
- Fenêtre d'affichage plein écran définie
- Fenêtre de visualisation secondaire définie (la fenêtre de rendu sera redimensionnée par rapport à sa fenêtre parente)
- CameraSelector spécifié
- RenderView (3)
- Fenêtre d’affichage en plein écran définie
- Fenêtre de visualisation secondaire définie (la fenêtre de rendu sera redimensionnée par rapport à sa fenêtre parente)
- CameraSelector spécifié
- RenderView (4)
- Fenêtre d'affichage en plein écran définie
- Fenêtre de visualisation secondaire définie (la fenêtre de rendu sera mise à l'échelle par rapport à sa fenêtre parente)
- CameraSelector spécifié
- RenderView (5)
- Fenêtre d'affichage en plein écran définie
- Fenêtre de visualisation secondaire définie (la fenêtre de rendu sera redimensionnée par rapport à sa fenêtre parente)
- CameraSelector spécifié
Cependant, dans ce cas, l’ordre est important. Si le nœud ClearBuffers se trouvait en dernier au lieu d’être le premier, cela entraînerait un écran noir pour la simple raison que tout serait effacé juste après avoir été si soigneusement rendu. Pour une raison similaire, il ne pourrait pas être utilisé comme racine du FrameGraph, car cela entraînerait un appel visant à effacer l'écran entier pour chacune de nos fenêtres de visualisation.
Bien que l’ordre de déclaration du FrameGraph soit important, Qt 3D est capable de traiter chaque RenderView en parallèle, car chaque RenderView est indépendante des autres pour ce qui est de générer un ensemble de RenderCommands à soumettre tant que l’état de la RenderView est actif.
Qt 3D utilise une approche du parallélisme basée sur les tâches, qui s’adapte naturellement au nombre de cœurs disponibles. Ceci est illustré dans le diagramme suivant pour l’exemple précédent.

Les RenderCommands des RenderViews peuvent être générées en parallèle sur plusieurs cœurs, et tant que l’on veille à soumettre les RenderViews dans le bon ordre sur le thread de soumission OpenGL dédié, la scène résultante sera correctement rendue.
Rendu différé
En matière de rendu, le rendu différé est très différent du rendu direct en termes de configuration du moteur de rendu. Au lieu de dessiner chaque maillage et d’appliquer un effet de shader pour l’ombrer, le rendu différé adopte une méthode à deux passes de rendu.
Tout d’abord, tous les maillages de la scène sont dessinés à l’aide du même shader qui génère, généralement pour chaque fragment, au moins quatre valeurs :
- Vecteur normal mondial
- Couleur (ou d’autres propriétés du matériau)
- Profondeur
- Vecteur de position dans l’espace
Chacune de ces valeurs sera stockée dans une texture. Les textures de normale, de couleur, de profondeur et de position forment ce que l'on appelle le G-Buffer. Rien n'est affiché à l'écran lors du premier passage ; les données sont plutôt enregistrées dans le G-Buffer en vue d'une utilisation ultérieure.
Une fois tous les maillages dessinés, le G-Buffer est rempli de tous les maillages actuellement visibles par la caméra. Le deuxième passage de rendu est alors utilisé pour rendre la scène dans le tampon arrière avec l’ombrage final, en lisant les valeurs de normale, de couleur et de position à partir des textures du G-Buffer et en affichant une couleur sur un quadrilatère en plein écran.
L’avantage de cette technique réside dans le fait que la puissance de calcul importante requise pour les effets complexes n’est mobilisée, lors du deuxième passage, que pour les éléments réellement visibles par la caméra. Le premier passage ne nécessite pas beaucoup de puissance de traitement, chaque maillage étant dessiné à l’aide d’un shader simple. Le rendu différé dissocie donc l’ombrage et l’éclairage du nombre d’objets présents dans une scène pour les lier à la résolution de l’écran (et du G-Buffer). Cette technique a été largement utilisée dans de nombreux jeux en raison de sa capacité à prendre en charge un grand nombre de lumières dynamiques, au prix d’une consommation supplémentaire de mémoire GPU.
Viewport {
id: root
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
property GBuffer gBuffer
property alias camera: sceneCameraSelector.camera
property alias sceneLayer: sceneLayerFilter.layers
property alias screenQuadLayer: screenQuadLayerFilter.layers
RenderSurfaceSelector {
CameraSelector {
id: sceneCameraSelector
// Fill G-Buffer
LayerFilter {
id: sceneLayerFilter
RenderTargetSelector {
id: gBufferTargetSelector
target: gBuffer
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
RenderPassFilter {
id: geometryPass
matchAny: FilterKey {
name: "pass"
value: "geometry"
}
}
}
}
}
TechniqueFilter {
parameters: [
Parameter { name: "color"; value: gBuffer.color },
Parameter { name: "position"; value: gBuffer.position },
Parameter { name: "normal"; value: gBuffer.normal },
Parameter { name: "depth"; value: gBuffer.depth }
]
RenderStateSet {
// Render FullScreen Quad
renderStates: [
BlendEquation { blendFunction: BlendEquation.Add },
BlendEquationArguments {
sourceRgb: BlendEquationArguments.SourceAlpha
destinationRgb: BlendEquationArguments.DestinationColor
}
]
LayerFilter {
id: screenQuadLayerFilter
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
RenderPassFilter {
matchAny: FilterKey {
name: "pass"
value: "final"
}
parameters: Parameter {
name: "winSize"
value: Qt.size(1024, 768)
}
}
}
}
}
}
}
}
}(Le code ci-dessus est adapté de qt3d/tests/manual/deferred-renderer-qml.)
D'un point de vue graphique, le framegraph obtenu se présente comme suit :

Et voici les RenderViews obtenues :
- RenderView (1)
- Spécifier la caméra à utiliser
- Définir une fenêtre d'affichage qui occupe tout l'écran
- Sélectionner toutes les entités du composant de calque sceneLayer
- Définir l’
gBuffercomme cible de rendu active - Effacer la couleur et la profondeur de la cible de rendu actuellement liée (
gBuffer) - Sélectionner uniquement les entités de la scène dont le matériau et la technique correspondent aux annotations du RenderPassFilter
- RenderView (2)
- Définir une fenêtre d’affichage qui occupe tout l’écran
- Sélectionne toutes les entités du composant de couche screenQuadLayer
- Efface les tampons de couleur et de profondeur sur le tampon d'image actuellement lié (l'écran)
- Sélectionner uniquement les entités de la scène dont le matériau et la technique correspondent aux annotations du RenderPassFilter
Autres avantages du framegraph
Comme l’arborescence FrameGraph est entièrement pilotée par les données et peut être modifiée dynamiquement lors de l’exécution, vous pouvez :
- Disposer de différents arbres FrameGraph pour différentes plateformes et différents matériels, et sélectionner le plus approprié lors de l’exécution
- Ajouter et activer facilement le débogage visuel dans une scène
- Utiliser différentes arborescences FrameGraph en fonction de la nature des éléments à rendre pour une région particulière de la scène
- Mettre en œuvre une nouvelle technique de rendu sans avoir à modifier le fonctionnement interne d’ Qt 3D
Conclusion
Nous avons présenté le FrameGraph et les types de nœuds qui le composent. Nous avons ensuite abordé quelques exemples pour illustrer les règles de construction du FrameGraph et la manière dont le moteur Qt 3D l'utilise en arrière-plan. À présent, vous devriez avoir une bonne vue d'ensemble du FrameGraph et de ses possibilités d'utilisation (par exemple pour ajouter un passage de remplissage Z précoce à un moteur de rendu direct). Gardez également toujours à l’esprit que le FrameGraph est un outil à votre disposition, qui vous permet de ne pas être limité au moteur de rendu et aux matériaux fournis par défaut par l’ Qt 3D.
© 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.