このページでは

シーングラフ - RHI テクスチャアイテム

QRhi でレンダリングされたテクスチャを表示する、カスタムQQuickItem の実装方法を説明します。

QRhi を使用してレンダリングされた RGB 三角形を QQuickItem として表示し、MSAA およびテクスチャオプションの設定パネルを備えています

この例では、QRhi API を使用して、クロスプラットフォームで移植性の高い 3D レンダリングをテクスチャに実行し、その画像を表示するアイテムの実装方法を示します。

注:この例は 、Qt GUI モジュールから互換性が限定的に保証されているAPIに依存しつつ、移植性のあるクロスプラットフォーム3Dレンダリングを行う、高度で低レベルの機能を実証するものです。QRhi APIを使用するには、アプリケーションをQt::GuiPrivate にリンクし、<rhi/qrhi.h> をインクルードする必要があります。

他のアプローチとの比較

「RHI Under QML」の例では、QRhi API を使用して、ポータブルでクロスプラットフォームな 3D レンダリングを実装する方法を示しています。この実装では、カスタムレンダリングがQt Quick のシーングラフによるレンダリングよりも先に実行され、事実上「アンダーレイ」として機能します。 このアプローチは、追加のレンダリングターゲットやレンダリングパスが不要であり、シーングラフ自身の描画呼び出しの前にメインのレンダリングパスにカスタムレンダリングが挿入されるため、効率的です。

対照的に、この例では、個別のレンダリングターゲット、QRhiTexture (そのdimensions はシーン内のQQuickItem のサイズと一致する)、およびそのテクスチャをクリアしてから描画するために使用されるレンダリングパス全体が使用されます。その後、メインのレンダリングパスでテクスチャがサンプリングされ、クワッドのテクスチャとして使用され、事実上2D画像が表示されます。

アンダーレイ/オーバーレイ方式と比較して、ここでは真のQQuickItem が利用可能であるため、3Dレンダリングの平坦化された2D画像を、Qt Quick シーン内の任意の場所に表示、ブレンド、変形させることができます。ただし、最初にテクスチャへのレンダリングを行う必要があるため、リソースとパフォーマンスの面で負荷が高くなります。

概要

このサンプルは、QQuickRhiItem およびQQuickRhiItemRenderer を使用して実装されています。QQuickRhiItem は、サブクラス化することで、内部でQSGSimpleTextureNode を使用し、QRhiTexture の内容を表示する、機能豊富なカスタムQQuickItem を簡単かつ迅速に作成できる利便性クラスです。テクスチャの内容は、アプリケーションが提供するロジックによって生成され、そのロジックはQQuickRhiItemRenderer サブクラスに実装されています。

ExampleRhiItem これは、`QQuickRhiItem ` のサブクラスであり、`angle ` や `backgroundAlpha` といったいくつかのプロパティを提供します。これらは QML から読み込み、書き込み、およびアニメーション処理が行われます。Qt Quick のスレッド化されたレンダリングモデルをサポートするため、QQQuickRhiItemRendererには仮想関数synchronize()が用意されており、これを再実装することで、QQuickRhiItem (メイン/GUIスレッドに属する)とQQuickRhiItemRenderer (レンダリングスレッドに属する、存在する場合)間のデータコピーを安全に行うことができます。

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()は、render()が最初に呼び出される前に少なくとも1回は呼び出されますが、実際には複数回呼び出される場合があります。これは、QQuickItem のジオメトリが変更された場合(レイアウトの変更、ウィンドウのサイズ変更など)、 など)、サンプル数やテクスチャ形式といったQQuickRhiItem の設定が変更された場合、あるいはアイテムが再親付けされて新しいQQuickWindow に属することになった場合など、これらはすべて、QQuickRhiItem によって管理されるリソースの1つ以上が変更されたことを意味し、サブクラスにも影響を及ぼすため、initialize()の再呼び出しをトリガーします。 ここでのサンプルコードは、これらの特殊な状況(QRhi の変更、サンプル数の変更、テクスチャ形式の変更)に対処できるよう用意されています。(カラーバッファとして使用されるテクスチャを保持していないため、サイズの変更によりテクスチャが再作成されるケースについては特別な処理は必要ありません)

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();
    }

initialize() の残りの部分は、QRhi に基づく単純明快なコードです。

3Dシーンでは透視投影を使用しており、これは出力サイズに基づいて計算されます。便宜上、QRhiRenderTarget から出力サイズを取得しています(これはマルチサンプリングの使用の有無にかかわらず機能する一方、colorTexture()やmsaaColorBuffer()にアクセスする場合は、どのオブジェクトが有効かによって分岐ロジックが必要になるためです)。

3DグラフィックスAPI間の座標系の違いに対応するために、QRhi::clipSpaceCorrMatrix() が使用されている点に注意してください。

    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);

render() の実装では、単一の三角形の描画を記録します。回転角が変化することが予想されるため、4x4 行列を含むユニフォームバッファは毎回更新されます。クリアカラーには、アイテムから提供された背景のアルファ値がベイクされています。赤、緑、青の各成分についても、アルファ値に事前乗算を行う必要があることに注意してください。

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();
}

サンプルプロジェクト @ code.qt.io

QQuickRhiItem 、シーングラフ - QML における RHI、およびシーングラフ - カスタム 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.