Szenendiagramm - RHI-Texturelement
Zeigt, wie man ein benutzerdefiniertes QQuickItem implementiert, das eine QRhi-gerenderte Textur anzeigt.
Dieses Beispiel zeigt, wie ein Element implementiert wird, das plattformübergreifendes, portables 3D-Rendering in eine Textur unter Verwendung der QRhi APIs durchführt und dieses Bild dann anzeigt.
Hinweis: Dieses Beispiel demonstriert eine fortgeschrittene Low-Level-Funktionalität, die portables, plattformübergreifendes 3D-Rendering durchführt und dabei auf APIs mit begrenzter Kompatibilitätsgarantie des Qt Gui-Moduls zurückgreift. Um die APIs von QRhi nutzen zu können, verlinkt die Anwendung auf Qt::GuiPrivate
und schließt <rhi/qrhi.h>
ein.
Vergleich mit anderen Ansätzen
Das RHI Under QML-Beispiel zeigt, wie portables, plattformübergreifendes 3D-Rendering mit den QRhi APIs in einer Weise implementiert werden kann, bei der das benutzerdefinierte Rendering vor dem eigenen Rendering des Qt Quick Szenegraphen ausgegeben wird, wodurch effektiv ein "Underlay" bereitgestellt wird. Dieser Ansatz ist effizient, da jetzt zusätzliche Rendering-Ziele und Rendering-Durchläufe benötigt werden, da das benutzerdefinierte Rendering in den Haupt-Rendering-Durchlauf vor den eigenen Zeichenaufrufen des Szenegraphen injiziert wird.
Im Gegensatz dazu umfasst dieses Beispiel ein separates Rendering-Ziel, eine QRhiTexture, deren dimensions der Größe der QQuickItem in der Szene entspricht, und einen ganzen Rendering-Durchgang, der zum Löschen und anschließenden Zeichnen in diese Textur verwendet wird. Die Textur wird dann im Haupt-Rendering-Durchgang abgetastet und zur Texturierung eines Quad verwendet, wodurch effektiv ein 2D-Bild angezeigt wird.
Im Vergleich zur Underlay/Overlay-Methode ermöglicht dies das Anzeigen, Überblenden und Transformieren des reduzierten 2D-Bildes des 3D-Renderings an beliebiger Stelle in der Szene Qt Quick, da wir hier eine echte QQuickItem haben. Der Preis dafür ist ein höherer Ressourcen- und Leistungsaufwand, da zuerst in eine Textur gerendert werden muss.
Überblick
Das Beispiel ist mit QQuickRhiItem und QQuickRhiItemRenderer implementiert. QQuickRhiItem ist eine Komfortklasse, die untergeordnet werden kann, um schnell und einfach eine voll funktionsfähige, benutzerdefinierte QQuickItem zu erhalten, die den Inhalt einer QRhiTexture anzeigt, indem QSGSimpleTextureNode unter der Haube verwendet wird. Der Inhalt der Textur wird durch die von der Anwendung bereitgestellte Logik erzeugt, die in der QQuickRhiItemRenderer Unterklasse implementiert ist.
ExampleRhiItem
ist eine QQuickRhiItem Unterklasse, die einige Eigenschaften bietet, wie angle
und backgroundAlpha
. Diese werden von QML gelesen, geschrieben und animiert. Um das threaded Rendering-Modell von Qt Quick zu unterstützen, verfügt der QQQuickRhiItemRenderer über eine virtuelle Funktion synchronize(), die neu implementiert werden kann, um das Kopieren von Daten zwischen QQuickRhiItem (das zum Haupt-/GUI-Thread gehört) und QQuickRhiItemRenderer (das zum Render-Thread gehört, falls es einen gibt) sicher durchzuführen.
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() wird mindestens einmal vor dem ersten Aufruf von render() aufgerufen, kann aber in der Praxis mehrfach aufgerufen werden: wenn sich die Geometrie von QQuickItem ändert (aufgrund einer Änderung des Layouts, der Größe des Fensters, etc.), wenn sich QQuickRhiItem Einstellungen wie die Anzahl der Samples und das Texturformat ändern, oder wenn das Element reparented wird, so dass es zu einer neuen QQuickWindow gehört. All dies löst einen erneuten Aufruf von initialize() aus, weil es bedeutet, dass sich eine oder mehrere der QQuickRhiItem-verwalteten Ressourcen ändern, was sich auch auf die Unterklasse auswirkt. Der Beispielcode hier ist darauf vorbereitet, mit diesen speziellen Situationen umzugehen (Änderung von QRhi, Änderung der Sampleanzahl, Änderung des Texturformats). (Da die als Farbpuffer verwendete Textur nicht gespeichert wird, muss der Fall, dass die Textur aufgrund einer anderen Größe neu erstellt wird, nicht speziell behandelt werden)
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(); }
Der Rest von initialize() ist unkomplizierter QRhi-basierter Code.
Die 3D-Szene verwendet eine perspektivische Projektion, die auf der Grundlage der Ausgabegröße berechnet wird, die der Einfachheit halber von QRhiRenderTarget abgefragt wird (da dies unabhängig davon funktioniert, ob Multisampling verwendet wird oder nicht, während der Zugriff auf colorTexture() und msaaColorBuffer() eine Verzweigungslogik erfordern würde, je nachdem, welches der Objekte gerade gültig ist).
Beachten Sie die Verwendung von QRhi::clipSpaceCorrMatrix(), um den Unterschieden im Koordinatensystem zwischen den 3D-Grafik-APIs Rechnung zu tragen.
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);
Die Implementierung von render() zeichnet das Zeichnen eines einzelnen Dreiecks auf. Der einheitliche Puffer mit der 4x4-Matrix wird jedes Mal aktualisiert, da wir erwarten, dass sich der Drehwinkel ändert. Die klare Farbe hat das vom Element bereitgestellte Hintergrund-Alpha eingebacken. Denken Sie daran, dass Sie den Alphawert auch in den Rot-, Grün- und Blaukomponenten vormultiplizieren müssen.
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(); }
Siehe auch QQuickRhiItem, Scene Graph - RHI Under QML, und Scene Graph - Custom QSGRenderNode.
© 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.