Graphique de scène - élément de texture RHI
Montre comment mettre en œuvre une QQuickItem personnalisée qui affiche une texture rendue à l'aide de QRhi.

Cet exemple montre comment mettre en œuvre un élément qui effectue un rendu 3D portable et multiplateforme dans une texture à l'aide des API QRhi et qui affiche ensuite cette image.
Remarque : cet exemple démontre une fonctionnalité avancée de bas niveau qui effectue un rendu 3D portable et multiplateforme, tout en s'appuyant sur des API avec une garantie de compatibilité limitée du module Qt GUI. Pour pouvoir utiliser les API de QRhi, l'application est liée à Qt::GuiPrivate et inclut <rhi/qrhi.h>.
Comparaison avec d'autres approches
L'exemple RHI Under QML montre comment mettre en œuvre un rendu 3D portable et multiplateforme avec les API QRhi de manière à ce que le rendu personnalisé soit émis avant le propre rendu du graphe de scène Qt Quick, fournissant ainsi une "sous-couche". Cette approche est efficace puisque maintenant que des cibles et des passes de rendu supplémentaires sont nécessaires, le rendu personnalisé est injecté dans la passe de rendu principale avant les appels de dessin du graphe de scène.
En revanche, cet exemple implique une cible de rendu distincte, un QRhiTexture, dont le dimensions correspond à la taille du QQuickItem dans la scène, et une passe de rendu complète utilisée pour effacer et dessiner dans cette texture. La texture est ensuite échantillonnée dans la passe de rendu principale et utilisée pour texturer un quad, affichant ainsi une image 2D.
Par rapport à l'approche par sous-couche/surcouche, cette méthode permet d'afficher, de mélanger et de transformer l'image 2D aplatie du rendu 3D n'importe où dans la scène Qt Quick, puisque nous disposons ici d'une véritable QQuickItem. Cette approche est cependant plus coûteuse en termes de ressources et de performances, car elle implique d'effectuer d'abord le rendu d'une texture.
Vue d'ensemble
L'exemple est implémenté à l'aide de QQuickRhiItem et QQuickRhiItemRenderer. QQuickRhiItem est une classe de commodité qui peut être sous-classée pour obtenir facilement et rapidement une QQuickItem personnalisée et complète qui affiche le contenu d'une QRhiTexture en utilisant QSGSimpleTextureNode sous le capot. Le contenu de la texture est généré par la logique fournie par l'application et mise en œuvre dans sa sous-classe QQuickRhiItemRenderer.
ExampleRhiItem est une sous-classe de QQuickRhiItem qui offre quelques propriétés, telles que angle et backgroundAlpha. Celles-ci seront lues, écrites et animées à partir de QML. Afin de prendre en charge le modèle de rendu threadé de Qt Quick, le QQQuickRhiItemRenderer dispose d'une fonction virtuelle synchronize() qui peut être réimplémentée pour effectuer en toute sécurité la copie des données entre QQuickRhiItem (appartenant au thread principal/GUI) et QQuickRhiItemRenderer (appartenant au thread de rendu, s'il y en a un).
QQuickRhiItemRenderer *ExampleRhiItem::createRenderer() { return new ExampleRhiItemRenderer; } void ExampleRhiItem::setAngle(float a) { if (m_angle == a) return; m_angle = a; emit angleChanged(); update(); } void ExampleRhiItem::setBackgroundAlpha(float a) { if (m_alpha == a) return; m_alpha = a; emit backgroundAlphaChanged(); update(); } void ExampleRhiItemRenderer::synchronize(QQuickRhiItem *rhiItem) { ExampleRhiItem *item = static_cast<ExampleRhiItem *>(rhiItem); if (item->angle() != m_angle) m_angle = item->angle(); if (item->backgroundAlpha() != m_alpha) m_alpha = item->backgroundAlpha(); }
initialize() est appelé au moins une fois avant le premier appel à render(), mais peut en pratique être invoqué plusieurs fois : si la géométrie de QQuickItem change (en raison d'une modification de la disposition, d'un redimensionnement de la fenêtre, etc.), si QQuickRhiItem les paramètres tels que le nombre d'échantillons et le format de texture changent, ou si l'élément est réparti de sorte qu'il appartienne à un nouveau QQuickWindow, tout cela déclenche un nouvel appel à initialize() car cela implique qu'une ou plusieurs des ressources gérées par QQuickRhiItem changent, ce qui aura également des implications sur la sous-classe. Le code d'exemple ici est préparé pour gérer ces situations spéciales (changement de QRhi, changement du nombre d'échantillons, changement du format de texture). (comme il ne conserve pas la texture utilisée comme tampon de couleur, le cas où la texture est recréée en raison d'une taille différente ne nécessite pas de traitement particulier).
void ExampleRhiItemRenderer::initialize(QRhiCommandBuffer *cb) { if (m_rhi != rhi()) { m_rhi = rhi(); m_pipeline.reset(); } if (m_sampleCount != renderTarget()->sampleCount()) { m_sampleCount = renderTarget()->sampleCount(); m_pipeline.reset(); } QRhiTexture *finalTex = m_sampleCount > 1 ? resolveTexture() : colorTexture(); if (m_textureFormat != finalTex->format()) { m_textureFormat = finalTex->format(); m_pipeline.reset(); }
Le reste de la fonction initialize() est un code direct basé sur QRhi.
La scène 3D utilise une projection en perspective, qui est calculée sur la base de la taille de sortie, demandée à partir de QRhiRenderTarget pour des raisons pratiques (parce que cela fonctionne indépendamment de l'utilisation du multi-échantillonnage ou non, alors que l'accès à colorTexture() et msaaColorBuffer() nécessiterait une logique de branchement basée sur lequel des objets est valide).
Notez l'utilisation de QRhi::clipSpaceCorrMatrix() pour tenir compte des différences de système de coordonnées entre les API graphiques 3D.
if (!m_pipeline) { m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData))); m_vbuf->create(); m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64)); m_ubuf->create(); m_srb.reset(m_rhi->newShaderResourceBindings()); m_srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, m_ubuf.get()), }); m_srb->create(); m_pipeline.reset(m_rhi->newGraphicsPipeline()); m_pipeline->setShaderStages({ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/scenegraph/rhitextureitem/shaders/color.vert.qsb")) }, { QRhiShaderStage::Fragment, getShader(QLatin1String(":/scenegraph/rhitextureitem/shaders/color.frag.qsb")) } }); QRhiVertexInputLayout inputLayout; inputLayout.setBindings({ { 5 * sizeof(float) } }); inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) } }); m_pipeline->setSampleCount(m_sampleCount); m_pipeline->setVertexInputLayout(inputLayout); m_pipeline->setShaderResourceBindings(m_srb.get()); m_pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor()); m_pipeline->create(); QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch(); resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData); cb->resourceUpdate(resourceUpdates); } const QSize outputSize = renderTarget()->pixelSize(); m_viewProjection = m_rhi->clipSpaceCorrMatrix(); m_viewProjection.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f); m_viewProjection.translate(0, 0, -4);
L'implémentation de render() enregistre le dessin d'un seul triangle. Le tampon uniforme avec la matrice 4x4 est mis à jour à chaque fois puisque nous nous attendons à ce que l'angle de rotation change. La couleur claire intègre l'alpha de l'arrière-plan fourni par l'élément. N'oubliez pas qu'il est nécessaire de prémultiplier la valeur alpha dans les composantes rouge, verte et bleue.
void ExampleRhiItemRenderer::render(QRhiCommandBuffer *cb) { QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch(); QMatrix4x4 modelViewProjection = m_viewProjection; modelViewProjection.rotate(m_angle, 0, 1, 0); resourceUpdates->updateDynamicBuffer(m_ubuf.get(), 0, 64, modelViewProjection.constData()); // Qt Quick expects premultiplied alpha const QColor clearColor = QColor::fromRgbF(0.5f * m_alpha, 0.5f * m_alpha, 0.7f * m_alpha, m_alpha); cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates); cb->setGraphicsPipeline(m_pipeline.get()); const QSize outputSize = renderTarget()->pixelSize(); cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height())); cb->setShaderResources(); const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); cb->setVertexInput(0, 1, &vbufBinding); cb->draw(3); cb->endPass(); }
Voir aussi QQuickRhiItem, Scene Graph - RHI Under QML, et Scene Graph - Custom QSGRenderNode.
© 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.