Szenengraph – RHI-Textur-Element
Zeigt, wie ein benutzerdefiniertes „ QQuickItem “ implementiert wird, das eine mit „ QRhi “ gerenderte Textur anzeigt.

Dieses Beispiel zeigt, wie ein Item implementiert wird, das mithilfe der QRhi -APIs plattformübergreifendes, portables 3D-Rendering in eine Textur durchführt und dieses Bild anschließend anzeigt.
Hinweis: Dieses Beispiel demonstriert fortgeschrittene, Low-Level-Funktionalität zur Durchführung eines portablen, plattformübergreifenden 3D-Renderings, wobei es sich auf APIs aus dem Qt GUI-Modul stützt, für die nur eine begrenzte Kompatibilitätsgarantie besteht. Um die QRhi -APIs nutzen zu können, muss die Anwendung mit „ Qt::GuiPrivate “ verknüpft sein und „ <rhi/qrhi.h> “ einbinden.
Vergleich mit anderen Ansätzen
Das Beispiel „RHI Under QML“ zeigt, wie man portables, plattformübergreifendes 3D-Rendering mit den „ QRhi “-APIs so implementiert, dass das benutzerdefinierte Rendering vor dem eigentlichen Rendering des Szenengraphen von „ Qt Quick “ ausgeführt wird und so effektiv eine „Unterlage“ bildet. Dieser Ansatz ist effizient, da nun keine zusätzlichen Renderziele und Renderdurchläufe mehr erforderlich sind; das benutzerdefinierte Rendering wird in den Hauptrenderdurchlauf vor den eigenen Zeichenaufrufen des Szenengraphen eingefügt.
Im Gegensatz dazu umfasst dieses Beispiel ein separates Renderziel, eine „ QRhiTexture “, deren „ dimensions “ der Größe des „ QQuickItem “ in der Szene entsprechen, sowie einen gesamten Renderdurchlauf, der zum Löschen und anschließenden Zeichnen in diese Textur verwendet wird. Die Textur wird dann im Hauptrenderdurchlauf abgetastet und zum Texturieren eines Quadrats verwendet, wodurch effektiv ein 2D-Bild angezeigt wird.
Im Vergleich zum Underlay-/Overlay-Ansatz ermöglicht dies die Darstellung, Überblendung und Transformation des abgeflachten 2D-Bildes des 3D-Renderings an beliebiger Stelle in der „ Qt Quick “-Szene, da wir hier über eine echte „ QQuickItem “ verfügen. Dies geht jedoch zu Lasten eines höheren Ressourcen- und Leistungsaufwands, da zunächst in eine Textur gerendert werden muss.
Übersicht
Das Beispiel wurde unter Verwendung von „ QQuickRhiItem “ und „ QQuickRhiItemRenderer “ implementiert. „ QQuickRhiItem “ ist eine Komfortklasse, die untervererbt werden kann, um einfach und schnell eine voll funktionsfähige, benutzerdefinierte „ QQuickItem “ zu erhalten, die den Inhalt eines „ QRhiTexture “ anzeigt, wobei im Hintergrund „ QSGSimpleTextureNode “ verwendet wird. Der Inhalt der Textur wird durch die anwendungsseitige Logik generiert, die in der Unterklasse „ QQuickRhiItemRenderer “ implementiert ist.
ExampleRhiItem ist eine Unterklasse von „ QQuickRhiItem “, die einige Eigenschaften bereitstellt, wie beispielsweise „ angle “ und „ backgroundAlpha “. Diese werden über QML gelesen, geschrieben und animiert. Um das Thread-basierte 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 dem ` QQuickRhiItem ` (der zum Haupt-/GUI-Thread gehört) und dem ` QQuickRhiItemRenderer ` (der zum Render-Thread gehört, falls vorhanden) 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 in der Praxis jedoch mehrfach aufgerufen werden: wenn sich die Geometrie von ` QQuickItem ` ändert (aufgrund einer Layoutänderung, einer Größenanpassung des Fensters, usw.), wenn sich „ QQuickRhiItem “-Einstellungen wie die Abtastanzahl und das Texturformat ändern oder wenn das Element neu zugeordnet wird, sodass es zu einem neuen „ QQuickWindow “ gehört – all dies löst einen erneuten Aufruf von „initialize()“ aus, da dies bedeutet, dass sich eine oder mehrere der von „ QQuickRhiItem “ verwalteten Ressourcen ändern, was auch Auswirkungen auf die Unterklasse hat. Der hier gezeigte Beispielcode ist darauf ausgelegt, diese besonderen Situationen zu bewältigen (Änderung von QRhi, Änderung der Sample-Anzahl, Änderung des Texturformats). (Da er nicht an der als Farbpuffer verwendeten Textur festhält, erfordert der Fall, in dem die Textur aufgrund einer anderen Größe neu erstellt wird, keine besondere Behandlung.)
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, auf ` QRhi` basierender Code.
Die 3D-Szene verwendet eine perspektivische Projektion, die auf der Grundlage der Ausgabegröße berechnet wird, die der Einfachheit halber aus dem ` 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, die davon abhängt, welches der Objekte gerade gültig ist).
Beachten Sie die Verwendung von QRhi::clipSpaceCorrMatrix(), um die Unterschiede im Koordinatensystem zwischen verschiedenen 3D-Grafik-APIs zu berücksichtigen.
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 Uniform-Buffer mit der 4x4-Matrix wird bei jedem Durchlauf aktualisiert, da wir davon ausgehen, dass sich der Rotationswinkel ändert. In der Clear-Farbe ist der vom Item bereitgestellte Hintergrund-Alpha-Wert bereits eingebacken. Beachten Sie, dass der Alpha-Wert auch in den Rot-, Grün- und Blau-Komponenten vorvervielfacht werden muss.
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“ , „Szenengraph – RHI unter QML“ und „Szenengraph – Benutzerdefinierter 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.