Qt Quick 3D - ステンシルアウトライン拡張の例
QtQuick3D Render Extensionを使用してステンシルアウトラインを実装する方法を示します。
この例では、QtQuick3D Render Extension を使用して、ステンシル・アウトラインのサポートを追加する方法を示します。
最初のステップは、QML に必要なプロパティを公開する新しいRender Extension アイテムを作成して、フロントエンド アイテムを実装することです。target
この例では3つのプロパティを公開しています。アウトライン化したいmodel 、アウトラインに使用したいmaterial 、アウトラインのサイズを調整するためのscale
。
class OutlineRenderExtension : public QQuick3DRenderExtension { Q_OBJECT Q_PROPERTY(QQuick3DObject * target READ target WRITE setTarget NOTIFY targetChanged) Q_PROPERTY(QQuick3DObject * outlineMaterial READ outlineMaterial WRITE setOutlineMaterial NOTIFY outlineMaterialChanged) Q_PROPERTY(float outlineScale READ outlineScale WRITE setOutlineScale NOTIFY outlineScaleChanged) QML_ELEMENT public: OutlineRenderExtension() = default; ~OutlineRenderExtension() override; float outlineScale() const; void setOutlineScale(float newOutlineScale); QQuick3DObject *target() const; void setTarget(QQuick3DObject *newTarget); QQuick3DObject *outlineMaterial() const; void setOutlineMaterial(QQuick3DObject *newOutlineMaterial); signals: void outlineColorChanged(); void outlineScaleChanged(); void targetChanged(); void outlineMaterialChanged(); protected: QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override; private: enum Dirty : quint8 { Target = 1 << 0, OutlineMaterial = 1 << 1, OutlineScale = 1 << 2 }; using DirtyT = std::underlying_type_t<Dirty>; void markDirty(Dirty v); QPointer<QQuick3DObject> m_target; QPointer<QQuick3DObject> m_outlineMaterial; float m_outlineScale = 1.05f; DirtyT m_dirtyFlag {}; };
2番目のステップは、バックエンドのRender Extension クラスを実装することです。これは、QtQuick3D によって実行されるコードを含むクラスです。
この拡張機能では、after
組み込みのカラーパスをレンダリングします。メインのレンダーパスの一部としてレンダリングしたいので、QSSGRenderExtension::stage() とQSSGRenderExtension::mode() 関数でそれぞれPostColor とMain を返します。
class OutlineRenderer : public QSSGRenderExtension { public: OutlineRenderer() = default; bool prepareData(QSSGFrameData &data) override; void prepareRender(QSSGFrameData &data) override; void render(QSSGFrameData &data) override; void resetForFrame() override; RenderMode mode() const override { return RenderMode::Main; } RenderStage stage() const override { return RenderStage::PostColor; }; QSSGPrepContextId stencilPrepContext { QSSGPrepContextId::Invalid }; QSSGPrepContextId outlinePrepContext { QSSGPrepContextId::Invalid }; QSSGPrepResultId stencilPrepResult { QSSGPrepResultId::Invalid }; QSSGPrepResultId outlinePrepResult { QSSGPrepResultId::Invalid }; QPointer<QQuick3DObject> model; QSSGNodeId modelId { QSSGNodeId::Invalid }; QPointer<QQuick3DObject> material; QSSGResourceId outlineMaterialId {}; float outlineScale = 1.05f; QSSGRenderablesId stencilRenderables; QSSGRenderablesId outlineRenderables; };
次に実装する必要がある関数はQSSGRenderExtension::prepareData() です。この関数は、このエクステンションがレンダリングに使用するデータを収集し、セットアップする必要があります。レンダリングするものがない場合、この関数はfalse
を返す。
bool OutlineRenderer::prepareData(QSSGFrameData &data) { // Make sure we have a model and a material. if (!model || !material) return false; modelId = QQuick3DExtensionHelpers::getNodeId(*model); if (modelId == QSSGNodeId::Invalid) return false; outlineMaterialId = QQuick3DExtensionHelpers::getResourceId(*material); if (outlineMaterialId == QSSGResourceId::Invalid) return false; // This is the active camera for the scene (the camera used to render the QtQuick3D scene) QSSGCameraId camera = data.activeCamera(); if (camera == QSSGCameraId::Invalid) return false; // We are going to render the same renderable(s) twice so we need to create two contexts. stencilPrepContext = QSSGRenderHelpers::prepareForRender(data, *this, camera, 0); outlinePrepContext = QSSGRenderHelpers::prepareForRender(data, *this, camera, 1); // Create the renderables for the target model. One for the original with stencil write, and one for the outline model. // Note that we 'Steal' the model here, that tells QtQuick3D that we'll take over the rendering of the model. stencilRenderables = QSSGRenderHelpers::createRenderables(data, stencilPrepContext, { modelId }, QSSGRenderHelpers::CreateFlag::Steal); outlineRenderables = QSSGRenderHelpers::createRenderables(data, outlinePrepContext, { modelId }); // Now we can start setting the data for our models. // Here we set a material and a scale for the outline QSSGModelHelpers::setModelMaterials(data, outlineRenderables, modelId, { outlineMaterialId }); QMatrix4x4 globalTransform = QSSGModelHelpers::getGlobalTransform(data, modelId); globalTransform.scale(outlineScale); QSSGModelHelpers::setGlobalTransform(data, outlineRenderables, modelId, globalTransform); // When all changes are done, we need to commit the changes. stencilPrepResult = QSSGRenderHelpers::commit(data, stencilPrepContext, stencilRenderables); outlinePrepResult = QSSGRenderHelpers::commit(data, outlinePrepContext, outlineRenderables); // If there's something to be rendered we return true. const bool dataReady = (stencilPrepResult != QSSGPrepResultId::Invalid && outlinePrepResult != QSSGPrepResultId::Invalid); return dataReady; }
QSSGRenderExtension::prepareData() がtrue
を返した場合、次に呼ばれる関数はQSSGRenderExtension::prepareRender() です。この関数では、2つのレンダラブル用にpipeline state をセットアップし、QSSGRenderHelpers::prepareRenderables() を呼び出してレンダラブル用のプリミティブなどを準備するようQtQuick3D に指示します。
void OutlineRenderer::prepareRender(QSSGFrameData &data) { Q_ASSERT(modelId != QSSGNodeId::Invalid); Q_ASSERT(stencilPrepResult != QSSGPrepResultId::Invalid && outlinePrepResult != QSSGPrepResultId::Invalid); const auto &ctx = data.contextInterface(); if (const auto &rhiCtx = ctx->rhiContext()) { const QSSGRhiGraphicsPipelineState basePs = data.getPipelineState(); QRhiRenderPassDescriptor *rpDesc = rhiCtx->mainRenderPassDescriptor(); const int samples = rhiCtx->mainPassSampleCount(); { // Original model - Write to the stencil buffer. QSSGRhiGraphicsPipelineState ps = basePs; ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, QSSGRhiGraphicsPipelineState::Flag::UsesStencilRef, QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled }; ps.stencilWriteMask = 0xff; ps.stencilRef = 1; ps.samples = samples; ps.cullMode = QRhiGraphicsPipeline::Back; ps.stencilOpFrontState = { QRhiGraphicsPipeline::Keep, QRhiGraphicsPipeline::Keep, QRhiGraphicsPipeline::Replace, QRhiGraphicsPipeline::Always }; QSSGRenderHelpers::prepareRenderables(data, stencilPrepResult, rpDesc, ps); } { // Scaled version - Only draw outside the original. QSSGRhiGraphicsPipelineState ps = basePs; ps.flags |= { QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, QSSGRhiGraphicsPipelineState::Flag::UsesStencilRef, QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled }; ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, false); ps.stencilWriteMask = 0; ps.stencilRef = 1; ps.cullMode = QRhiGraphicsPipeline::Back; ps.stencilOpFrontState = { QRhiGraphicsPipeline::Keep, QRhiGraphicsPipeline::Keep, QRhiGraphicsPipeline::Replace, QRhiGraphicsPipeline::NotEqual }; QSSGRenderHelpers::prepareRenderables(data, outlinePrepResult, rpDesc, ps); } } }
エンジンが拡張機能のレンダリング呼び出しを記録する準備ができたら、仮想QSSGRenderExtension::render ()関数を呼び出します。この例では、2つのモデルに対してQSSGRenderHelpers::renderRenderables() を呼び出すだけで、QtQuick3D と同じようにレンダリングされます。
void OutlineRenderer::render(QSSGFrameData &data) { Q_ASSERT(stencilPrepResult != QSSGPrepResultId::Invalid); const auto &ctx = data.contextInterface(); if (const auto &rhiCtx = ctx->rhiContext()) { QRhiCommandBuffer *cb = rhiCtx->commandBuffer(); cb->debugMarkBegin(QByteArrayLiteral("Stencil outline pass")); QSSGRenderHelpers::renderRenderables(data, stencilPrepResult); QSSGRenderHelpers::renderRenderables(data, outlinePrepResult); cb->debugMarkEnd(); } }
OutlineRenderExtension
は、View3D's extensions プロパティに追加することでアクティブになります。
View3D { id: view3d anchors.topMargin: 100 anchors.fill: parent extensions: [ OutlineRenderExtension { id: outlineRenderer outlineMaterial: outlineMaterial } ]
これで、model
がピックされたら、ピックされたmodel
をOutlineRenderExtension
のtarget
として設定するだけで、アウトライン付きでレンダリングされるようになります。
MouseArea { anchors.fill: view3d onClicked: (mouse)=> { let hit = view3d.pick(mouse.x, mouse.y) outlineRenderer.target = hit.objectHit } }
© 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.