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

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

この例では、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シーンのどこにでも表示、ブレンド、変換することができます。これは、最初にテクスチャにレンダリングする必要があるため、リソースとパフォーマンスの面でより高価になります。

概要

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

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

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 に属するように再ペアレントされた場合、これらはすべてinitialize()を再度呼び出すトリガとなります。これは、QQuickRhiItem で管理されるリソースの1つ以上が変更されることを意味するためで、サブクラスにも影響を与えることになります。ここでは、これらの特殊な状況(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シーンは透視投影を使用します。透視投影は出力サイズに基づいて計算され、便宜上QRhiRenderTargetcolorTexture ()および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() の実装は、1つの三角形の描画を記録します。回転角度が変化することが予想されるため、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

QQuickRhiItemScene Graph - RHI Under QMLScene Graph - Custom QSGRenderNodeも参照して ください。

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。