Item.layer.effect

2025-06-06

通常、QMLのアイテムは、それが属するウィンドウに直接描画されます。しかし、layer.enabled: trueを設定することで、そのアイテムとその子孫全体をオフスクリーンサーフェス(テクスチャ)に描画させることができます。このオフスクリーンサーフェスは、その後ウィンドウに描画されます。

layer.effectプロパティは、このオフスクリーンサーフェスに対して、さらに追加の視覚効果を適用するために使用されます。具体的には、ShaderEffect型のコンポーネントを指定することが一般的です。

  1. レイヤーの有効化(layer.enabled: layer.effectを使うためには、まずlayer.enabled: trueを設定して、アイテムをオフスクリーンレンダリングするようにする必要があります。これにより、アイテムとその子要素が単一のテクスチャとして扱われ、そのテクスチャに対してエフェクトが適用されます。

  2. ShaderEffectとの組み合わせ: layer.effectの最も一般的な使用方法は、ShaderEffect型のアイテムを割り当てることです。ShaderEffectは、カスタムのOpenGLシェーダー(バーテックスシェーダーとフラグメントシェーダー)を適用することで、ドロップシャドウ、ブラー(ぼかし)、色付け、ページめくりなどの様々な視覚効果をQMLで直接記述することを可能にします。

    • ShaderEffectに渡されるテクスチャは、layer.enabledによって生成されたアイテムのオフスクリーンレンダリング結果となります。
    • シェーダー内で、このテクスチャをsampler2D型のuniform変数として受け取り、カスタムの描画ロジックを実装します。
  3. パフォーマンスとメモリ:

    • layer.enabledtrueに設定すると、そのアイテムの幅×高さ×4バイト(RGBA)のメモリがGPU上に確保されます。大きなアイテムにレイヤーを適用する場合は、メモリ使用量に注意が必要です。
    • レイヤーを使用するアイテムは、レンダリング時にバッチ処理されないため、多数のレイヤー化されたアイテムがあるシーンではパフォーマンスの問題が発生する可能性があります。
    • そのため、通常は視覚効果が必要な間だけlayer.enabledtrueにし、効果が不要になったらfalseに戻すことが推奨されます。
  4. 使用例:

    import QtQuick 2.0
    import QtGraphicalEffects 1.0 // エフェクトモジュールをインポート
    
    Rectangle {
        width: 200
        height: 200
        color: "lightblue"
    
        Text {
            text: "Hello Layer Effect!"
            font.pixelSize: 30
            anchors.centerIn: parent
        }
    
        // レイヤーを有効にし、そのレイヤーにエフェクトを適用
        layer.enabled: true
        layer.effect: DropShadow { // ドロップシャドウ効果を適用
            anchors.fill: parent
            verticalOffset: 5
            horizontalOffset: 5
            radius: 8
            color: "#80000000" // 半透明の黒
        }
    }
    

    この例では、Rectangleとその中のTextがオフスクリーンで描画され、その結果に対してDropShadow(ドロップシャドウ)効果が適用されます。



エフェクトが表示されない、または期待通りに動作しない

一般的な原因

  • エフェクトの配置とアンカー
    • layer.effect 自体もQMLアイテムとして扱われるため、親アイテムに対して適切に配置(anchors.fill: parent など)されているか確認してください。特に、エフェクトが適用される範囲が親アイテムのサイズと一致しているか重要です。
    • エフェクトが親アイテムの境界からはみ出すような場合(例: DropShadow の影が外に出る)、親アイテムの clip: true が設定されていないか確認してください。clip: true が設定されていると、はみ出した部分は描画されません。
  • ShaderEffect のシェーダーエラー
    • カスタムの ShaderEffect を使用している場合、シェーダーコード(GLSL)に文法エラーがあるか、GPUがサポートしない機能を使用している可能性があります。
    • qmlscene でQMLファイルをテストしたり、Qt Creator の QML Debugger を使用して、シェーダーのコンパイルエラーや実行時エラーのメッセージを確認してください。
    • mainTexture など、シェーダーに渡される uniform 変数の名前が正しいか確認してください。layer.effect の場合、通常、元のアイテムの描画結果は source または mainTexture (シェーダーの実装による) として渡されます。
  • QtGraphicalEffects モジュールのインポート忘れ
    DropShadowGaussianBlur など、Qt Quick に含まれる標準のエフェクトを使用する場合、QMLファイルに import QtGraphicalEffects 1.0 (または適切なバージョン) を記述しているか確認してください。
  • layer.enabled: false または未設定
    layer.effect を使用するには、必ず layer.enabled: true を設定し、アイテムをオフスクリーンレンダリングするようにする必要があります。これが最も基本的な要件です。

トラブルシューティング

  • clip: true が問題の原因ではないか確認する。
  • エフェクトアイテムの widthheight、および anchors が親アイテムのサイズと位置に適切に設定されているか確認する。
  • Qt Creator の QML Debugger を使って、シェーダーのコンパイルエラーや実行時エラーの出力を確認する。
  • ShaderEffect を使用している場合は、シンプルなシェーダーで試してみて、徐々に複雑にしていく。
  • 必要な import ステートメントがすべて存在するか確認する。
  • layer.enabled: true を明示的に追加する。

パフォーマンスの問題(フレームレートの低下)

一般的な原因

  • 複雑なシェーダーエフェクト
    ShaderEffect 内のシェーダーコードが非常に複雑な計算を行っている場合、GPUの負荷が大きくなり、フレームレートが低下します。特に、ピクセルごとに複雑な計算を行うフラグメントシェーダーは注意が必要です。
  • 頻繁なレイヤーの再レンダリング
    アイテムのジオメトリやコンテンツが頻繁に変化すると、オフスクリーンバッファの内容が毎フレーム更新されるため、パフォーマンスが低下します。例えば、アニメーション中のアイテムにレイヤーを適用すると、そのアイテムが動くたびにレイヤー全体が再描画されます。
  • 過剰なオフスクリーンレンダリング
    layer.enabled: true は、そのアイテムとそのすべての子孫をオフスクリーンバッファ(テクスチャ)にレンダリングするため、計算コストとメモリコストが発生します。
    • 大きなアイテム
      解像度の高いアイテムや非常に大きいアイテムにレイヤーを適用すると、大量のGPUメモリとレンダリング時間が必要になります。
    • 多数のレイヤー化されたアイテム
      シーン内に layer.enabled: true が設定されたアイテムが多数ある場合、それぞれのアイテムが個別のテクスチャとしてレンダリングされるため、描画パスが増え、GPUに負荷がかかります。

トラブルシューティング

  • Qt Quick Scene Graph のツールを使用する
    • QML_SCENE_GRAPH_DEBUG=render 環境変数を設定してアプリケーションを実行すると、シーングラフのレンダリング情報をデバッグ出力として確認できます。これにより、どのアイテムがどのように描画されているか、再レンダリングが頻繁に発生しているアイテムはどれかなどを特定できます。
    • Qt Creator の QML Profiler を使用して、レンダリングパスやボトルネックを詳細に分析する。
  • シェーダーの最適化
    ShaderEffect を使用している場合、シェーダーコードを可能な限り最適化する。不要な計算を省いたり、より効率的なアルゴリズムを使用したりする。
  • レイヤーを適用するアイテムの粒度を見直す
    • コンテナアイテム全体にレイヤーを適用するのではなく、本当にエフェクトが必要な部分にだけレイヤーを適用できないか検討する。
    • 静的なコンテンツ(背景など)にはレイヤーを使わず、動的なコンテンツにのみ使用することを検討する。
  • 必要な場合にのみ layer.enabled: true にする
    アニメーションの開始時など、視覚効果が必要な短い期間だけ layer.enabledtrue に設定し、効果が不要になったら false に戻すことで、リソースの消費を抑えます。

メモリ使用量の増加

一般的な原因

  • 多数のレイヤー化されたアイテム
    パフォーマンスの問題と同様に、多数のアイテムにレイヤーを適用すると、それぞれのアイテムが個別のテクスチャを生成するため、GPUメモリを大量に消費します。
  • 大きなテクスチャの生成
    layer.enabled: true が設定されたアイテムは、その幅と高さに基づいてRGBA形式のテクスチャをGPUメモリに作成します。特に高DPIディスプレイを使用している場合、論理ピクセル数よりも実際のピクセル数が増えるため、さらに大きなメモリを消費します。

トラブルシューティング

  • リソースの共有
    複数のアイテムで同じシェーダーエフェクトを使用する場合、ShaderEffect のインスタンスを共有できないか検討する。
  • 不要になったレイヤーは無効にする
    layer.enabled: false に戻すことで、関連するGPUメモリが解放されます。
  • レイヤーを適用するアイテムのサイズを最小限にする
    不必要に大きなアイテムにレイヤーを適用しない。
  • レイヤーの必要性を再評価
    本当に layer.effect が必要なのか、あるいはよりシンプルな方法(例: 境界線や影を直接描画するなど)で同等の効果を実現できないか検討する。

一般的な原因

  • ShaderEffect 内でのアニメーション
    ShaderEffect 内で UniformAnimator などを使用してアニメーションを行う場合、QMLの通常の Animation との連携が難しい場合があります。特に、layer.effect に設定された ShaderEffect は、親アイテムがオフスクリーンにレンダリングされた後、そのテクスチャに対して処理が行われるため、通常のQMLアニメーションのタイミングとズレる可能性があります。

トラブルシューティング

  • ShaderEffectSource の利用
    複数の異なるアイテムに対して同じエフェクトを適用したいが、それぞれに独立したアニメーションをさせたい場合など、ShaderEffectSource を使用して、QMLツリー内の特定のアイテムをエフェクトの入力として明示的に指定する方法もあります。
  • QMLのトップレベルのアニメーションを使用する
    可能な限り、ShaderEffect 内部ではなく、layer.effect が適用される親アイテムのプロパティをアニメーションさせることを検討してください。例えば、シェーダーのuniform変数を、親アイテムのプロパティにバインドし、その親アイテムのプロパティをアニメーションさせる方法です。

Item.layer.effect を使用する際は、以下の点を常に意識することが重要です。

  • シェーダーコードは慎重に記述し、最適化を心がける。
  • デバッグツール(QML Debugger, QML Profiler, QML_SCENE_GRAPH_DEBUG)を活用する。
  • パフォーマンスとメモリ使用量に注意する。 特に、大きなアイテムや多数のアイテムに適用する場合、または頻繁に更新されるコンテンツに適用する場合。
  • layer.enabled: true は必須。


基本的な構造

Item.layer.effect を使用する際の基本的な構造は以下のようになります。

import QtQuick 2.0
import QtGraphicalEffects 1.0 // 標準エフェクトを使用する場合に必要

Item {
    width: 200
    height: 150
    // ... その他のプロパティ

    // layer.enabled を true に設定することが必須
    layer.enabled: true

    // layer.effect にエフェクトアイテムを割り当てる
    layer.effect: SomeEffect {
        // エフェクトのプロパティ
        // 通常、エフェクトのソースは自動的に Item のレイヤー内容になる
        // anchors.fill: parent // 親アイテムと同じサイズにエフェクトを適用する場合
    }

    // エフェクトを適用したい子アイテム群
    Rectangle {
        width: 100
        height: 100
        color: "red"
        anchors.centerIn: parent
        Text {
            text: "Layered!"
            font.pixelSize: 20
            color: "white"
            anchors.centerIn: parent
        }
    }
}

この例では、Item の中に配置された RectangleText がオフスクリーンテクスチャに描画され、そのテクスチャ全体に対して SomeEffect が適用されます。

例1: ドロップシャドウ (DropShadow)

最も一般的なエフェクトの一つがドロップシャドウです。QtGraphicalEffects モジュールに含まれる DropShadow を使用します。

QML コード (main.qml)

import QtQuick 2.0
import QtGraphicalEffects 1.0 // DropShadow を使うためにインポート

Rectangle {
    width: 400
    height: 300
    color: "#f0f0f0" // 背景色

    Rectangle {
        id: myBox
        width: 150
        height: 100
        color: "deepskyblue"
        radius: 10 // 角を丸くする
        anchors.centerIn: parent

        // このアイテムとその子孫をオフスクリーンにレンダリング
        layer.enabled: true

        // レイヤーにドロップシャドウ効果を適用
        layer.effect: DropShadow {
            // エフェクトのソースは自動的に myBox のレイヤーになる
            anchors.fill: myBox // ドロップシャドウが myBox のサイズに合わせて広がるように設定
            horizontalOffset: 8 // 横方向の影のずれ
            verticalOffset: 8   // 縦方向の影のずれ
            radius: 15          // 影のぼかし具合 (大きいほど広がる)
            color: "#80000000"  // 影の色 (半透明の黒)
            samples: 17         // 影の品質 (大きいほど滑らかだが重い)
        }

        Text {
            text: "Shadow Box"
            font.pixelSize: 24
            color: "white"
            anchors.centerIn: parent
        }
    }
}

解説

  • samples プロパティは、ぼかしの品質に影響します。値を大きくすると品質が向上しますが、パフォーマンスが低下します。
  • anchors.fill: myBox は、DropShadow エフェクトが myBox の領域に合わせて拡大されるように設定しています。これにより、影がmyBoxの外側に適切に表示されます。
  • layer.effectDropShadow を割り当て、影の色、位置、ぼかし具合などを設定しています。
  • myBoxlayer.enabled: true が、そのアイテム全体(RectangleText)を単一の画像としてオフスクリーンバッファに描画します。

例2: 複数のエフェクト (MultiEffect - Qt 6.5以降推奨)

Qt 6.5 以降では、複数のエフェクトを効率的に適用するために QtQuick.Effects モジュールの MultiEffect が推奨されます。これは、複数の一般的なエフェクト(ブラー、彩度、明るさ、影など)を単一のシェーダーパスで適用できるため、パフォーマンスが向上します。

QML コード (main.qml)

import QtQuick 2.0
import QtQuick.Effects 1.0 // MultiEffect を使うためにインポート (Qt 6.5以降)

Rectangle {
    width: 400
    height: 300
    color: "#e0e0e0"

    Image {
        id: imageWithEffects
        source: "https://via.placeholder.com/150/FF5733/FFFFFF?text=Awesome" // 適当な画像URL
        width: 150
        height: 150
        anchors.centerIn: parent
        smooth: true // 画像の滑らかさを有効にする

        layer.enabled: true
        layer.effect: MultiEffect {
            // ブラー効果
            blurEnabled: true
            blur: 0.5 // 0.0 (なし) から 1.0 (最大)
            blurMax: 32 // 最大ぼかし半径

            // 彩度調整
            saturation: 0.2 // -1.0 (白黒) から 無限 (高い彩度)

            // ドロップシャドウ
            shadowEnabled: true
            shadowHorizontalOffset: 5
            shadowVerticalOffset: 5
            shadowBlur: 0.8
            shadowColor: "#80000000"

            // 自動パディングを有効にして、影やぼかしがクリップされないようにする
            autoPaddingEnabled: true
        }
    }
}

解説

  • QtGraphicalEffects の個別のエフェクト(DropShadow, GaussianBlur など)も引き続き使用できますが、複数のエフェクトを組み合わせる場合は MultiEffect の方が効率的です。
  • autoPaddingEnabled: true は、影やぼかしがアイテムの境界線からはみ出す可能性がある場合に、自動的にアイテムのサイズを拡張してクリップされないようにします。これは非常に便利です。
  • MultiEffect は、blurEnabled, saturation, shadowEnabled などのプロパティを組み合わせて、複数のエフェクトを一度に適用できます。

例3: カスタムシェーダーエフェクト (ShaderEffect)

より複雑な、あるいは独自の視覚効果を実装したい場合は、ShaderEffect を使用してカスタムのGLSL(OpenGL Shading Language)シェーダーを記述します。

この例では、アイテムの色を反転させるシンプルなシェーダーを作成します。

QML コード (main.qml)

import QtQuick 2.0

Rectangle {
    width: 400
    height: 300
    color: "#f0f0f0"

    Column {
        anchors.centerIn: parent
        spacing: 20

        Rectangle {
            id: originalRect
            width: 150
            height: 100
            color: "blue"
            Text {
                text: "Original"
                font.pixelSize: 20
                color: "white"
                anchors.centerIn: parent
            }
        }

        Rectangle {
            id: invertedRect
            width: 150
            height: 100
            color: "red" // レイヤーによって上書きされる
            Text {
                text: "Inverted"
                font.pixelSize: 20
                color: "white"
                anchors.centerIn: parent
            }

            layer.enabled: true
            layer.effect: ShaderEffect {
                // fragmentShader プロパティに直接 GLSL コードを記述
                // またはファイルパスを指定する (例: "qrc:/shaders/invert.frag")
                // Qt 6以降は.qsb形式が推奨されるが、ここでは単純化のため直接記述
                fragmentShader: "
                    // Qt Quick の組み込みuniform変数を宣言
                    uniform lowp sampler2D source; // レイヤーのテクスチャ (元のアイテムの描画結果)
                    uniform highp float qt_Opacity; // アイテムの結合された不透明度

                    varying highp vec2 qt_TexCoord0; // テクスチャ座標

                    void main() {
                        // ソーステクスチャから現在のピクセルの色をサンプリング
                        lowp vec4 color = texture2D(source, qt_TexCoord0);

                        // 色を反転させる (RGB成分のみ)
                        color.rgb = vec3(1.0, 1.0, 1.0) - color.rgb;

                        // 結果の色を出力
                        gl_FragColor = color * qt_Opacity; // 不透明度を適用
                    }
                "
                // ShaderEffect の width と height は、通常は親アイテムと同じにする
                width: invertedRect.width
                height: invertedRect.height
            }
        }
    }
}
  • gl_FragColor に最終的なピクセル色を設定します。qt_Opacity を掛けることで、QMLアイテムの不透明度設定がシェーダーに反映されます。
  • color.rgb = vec3(1.0, 1.0, 1.0) - color.rgb; で、RGB値を反転させています。
  • texture2D(source, qt_TexCoord0) で、元のテクスチャからピクセルの色をサンプリングします。
  • varying highp vec2 qt_TexCoord0; は、頂点シェーダーからフラグメントシェーダーに補間されて渡されるテクスチャ座標です。
  • uniform lowp sampler2D source; は、layer.enabled によって生成されたオフスクリーンテクスチャ(元のアイテムの描画結果)を受け取るための uniform 変数です。この変数は自動的に設定されます。
  • ShaderEffectfragmentShader プロパティに、GLSLのフラグメントシェーダーコードを直接記述しています。
  • Qt 6.5以降の MultiEffect
    Qt 6.5 以降では、QtQuick.Effects モジュールの MultiEffect が導入され、複数のエフェクトをより効率的に適用できるようになりました。可能であればこちらを利用することを検討してください。
  • QtGraphicalEffects モジュール
    DropShadowGaussianBlur など、Qt Quick に組み込まれているグラフィカルエフェクトは、QtGraphicalEffects モジュールに属しています。これらを使用するには、QMLファイルで import QtGraphicalEffects 1.0 (または適切なバージョン) を行う必要があります。
  • メモリ使用量
    オフスクリーンテクスチャはGPUメモリを消費します。解像度の高いアイテムや多数のレイヤー化されたアイテムは、多くのメモリを必要とします。
  • パフォーマンス
    layer.enabled: true はオフスクリーンレンダリングを行うため、特に大きなアイテムや多数のアイテムに適用すると、パフォーマンスに影響を与える可能性があります。必要な場合にのみ使用し、不要になったら layer.enabled: false に戻すことを検討してください。


Item.layer.effect の内部実装: ShaderEffectSource を直接利用する

Item.layer.enabled は、実際には内部的に ShaderEffectSource を使用してアイテムのレンダリング結果をテクスチャとして取得しています。したがって、より細かく制御したい場合や、複数のシェーダーに同じソースを供給したい場合などは、ShaderEffectSource を直接使用することができます。

特徴

  • sourceRecttextureSize など、レイヤーテクスチャの生成に関するより詳細な制御が可能です。
  • ShaderEffectSource は、QML ツリー内の任意の Item をソースとして指定できます。これにより、特定のアイテムのレンダリング結果を複数の ShaderEffect に渡すことができます。
  • Item.layer.effect と同じくオフスクリーンレンダリングを活用するため、パフォーマンス特性は似ています。

使用例

import QtQuick 2.0

Rectangle {
    width: 400
    height: 300
    color: "#f0f0f0"

    // ソースとなるアイテム
    Rectangle {
        id: originalContent
        width: 150
        height: 100
        color: "blue"
        anchors.centerIn: parent

        Text {
            text: "Hello!"
            font.pixelSize: 30
            color: "white"
            anchors.centerIn: parent
        }
    }

    // ShaderEffectSource を使って originalContent をテクスチャ化
    ShaderEffectSource {
        id: contentSource
        sourceItem: originalContent // ソースアイテムを指定
        live: true // ソースアイテムが変更されたらテクスチャを更新
        // 以下は必要に応じて設定
        // textureSize: Qt.size(originalContent.width, originalContent.height)
        // sourceRect: originalContent.mapToItem(null, originalContent.x, originalContent.y, originalContent.width, originalContent.height)
    }

    // テクスチャ化した contentSource を使ってエフェクトを適用
    ShaderEffect {
        // fragmentShader の中に `source` という uniform sampler2D 変数で contentSource を受け取る
        fragmentShader: "
            uniform lowp sampler2D source;
            varying highp vec2 qt_TexCoord0;
            void main() {
                // 色を反転させる (例)
                lowp vec4 color = texture2D(source, qt_TexCoord0);
                gl_FragColor = vec4(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, color.a);
            }
        "
        source: contentSource // ここで ShaderEffectSource をシェーダーの source にバインド
        width: originalContent.width
        height: originalContent.height
        x: originalContent.x + 100 // 位置をずらして表示
        y: originalContent.y
    }
}

利点

  • 複数のエフェクトで同じソーステクスチャを共有できる。
  • より柔軟なソースの指定。

欠点

  • オフスクリーンレンダリングに伴うパフォーマンスとメモリの考慮は同じ。
  • layer.effect よりも冗長な記述が必要。

QML の標準プロパティとアニメーションを直接使用する

シンプルな視覚効果(透明度、拡大縮小、回転、色変更など)であれば、Item.layer.effect を使わずに、QML の標準プロパティと AnimationTransition を直接使用する方が効率的です。

特徴

  • 基本的なUI要素のインタラクションに適している。
  • 宣言的で理解しやすいコードになる。
  • GPUにオフスクリーンバッファを生成しないため、メモリとパフォーマンスのオーバーヘッドが小さい。

使用例

import QtQuick 2.0

Rectangle {
    width: 400
    height: 300
    color: "#f0f0f0"

    Rectangle {
        id: myButton
        width: 120
        height: 50
        color: "green"
        radius: 5
        anchors.centerIn: parent

        Text {
            text: "Click Me!"
            font.pixelSize: 20
            color: "white"
            anchors.centerIn: parent
        }

        // クリック時に透明度とスケールをアニメーション
        MouseArea {
            anchors.fill: parent
            onClicked: {
                myButton.opacity = 0.5
                myButton.scale = 1.2
            }
        }

        // アニメーションの定義
        PropertyAnimation on opacity { to: 1.0; duration: 200 }
        PropertyAnimation on scale { to: 1.0; duration: 200 }
    }
}

利点

  • 実装がシンプル。
  • 軽量で高速。

欠点

  • ドロップシャドウ、ブラー、カスタムシェーダーなどの複雑なグラフィカルエフェクトには対応できない。

C++ でカスタム QQuickItem を作成する (QQuickPaintedItem / QQuickItem のサブクラス)

より高度な描画や、特定のパフォーマンス最適化が必要な場合、または Qt Quick の描画パイプラインに深く関与したい場合は、C++ で QQuickPaintedItem または QQuickItem をサブクラス化する方法があります。

  • QQuickItem のサブクラス化: updatePaintNode() などのメソッドをオーバーライドして、OpenGL (または他のグラフィックスAPI) のプリミティブを直接描画する QSGNode を構築します。これは最も柔軟で高性能な方法ですが、OpenGL/グラフィックスAPIの知識が必要です。カスタムシェーダーを直接適用したり、特殊なレンダリング技術を実装したりできます。
  • QQuickPaintedItem: Qt Widgets の QWidget::paintEvent() のように QPainter を使って描画を行うアイテムを作成するのに適しています。簡単なカスタム描画には便利ですが、内部的にはCPUで描画された後、GPUにアップロードされるため、非常に複雑な描画や頻繁な更新には向かない場合があります。

特徴

  • C++ で複雑な計算やデータ処理を行い、その結果を直接描画に反映できる。
  • 最高レベルのパフォーマンスと描画制御が可能。
  • C++ の力をフル活用できる。

使用例(QQuickPaintedItem の簡単な例 - シャドウを自分で描画する)

C++ コード (myshadowitem.h)

#ifndef MYSHADOWITEM_H
#define MYSHADOWITEM_H

#include <QQuickPaintedItem>
#include <QColor>

class MyShadowItem : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(QColor shadowColor READ shadowColor WRITE setShadowColor NOTIFY shadowColorChanged)
    Q_PROPERTY(qreal shadowRadius READ shadowRadius WRITE setShadowRadius NOTIFY shadowRadiusChanged)

public:
    MyShadowItem(QQuickItem *parent = nullptr);

    void paint(QPainter *painter) override;

    QColor shadowColor() const { return m_shadowColor; }
    void setShadowColor(const QColor &color);

    qreal shadowRadius() const { return m_shadowRadius; }
    void setShadowRadius(qreal radius);

signals:
    void shadowColorChanged();
    void shadowRadiusChanged();

private:
    QColor m_shadowColor;
    qreal m_shadowRadius;
};

#endif // MYSHADOWITEM_H

C++ コード (myshadowitem.cpp)

#include "myshadowitem.h"
#include <QPainter>
#include <QBrush>
#include <QGraphicsDropShadowEffect> // Qt::DropShadow を模倣する場合など

MyShadowItem::MyShadowItem(QQuickItem *parent)
    : QQuickPaintedItem(parent),
      m_shadowColor(Qt::black),
      m_shadowRadius(0.0)
{
    // アイテムの描画をキャッシュする場合は setCachingAllowed(true)
    // ただし、頻繁に変わる場合は避ける
}

void MyShadowItem::paint(QPainter *painter)
{
    // ここで独自の描画ロジックを実装
    // 例えば、シャドウを自分で描画する
    if (m_shadowRadius > 0) {
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setPen(Qt::NoPen);

        // シャドウをぼかすために QGraphicsDropShadowEffect を使うのはQPainterには直接効かない
        // 通常は、QPainterでグラデーションや複数の円を重ねるなどでぼかしを再現するか
        // 別の方法(例: QMLレイヤーエフェクト)に頼る
        // ここでは簡単なぼかしなしの影の例
        QColor shadow = m_shadowColor;
        shadow.setAlphaF(shadow.alphaF() * 0.5); // 半透明にする
        painter->setBrush(shadow);
        painter->drawRect(x() + m_shadowRadius, y() + m_shadowRadius, width(), height()); // 影をずらして描画
    }

    // 元のアイテムの描画 (この例では単純な長方形)
    painter->setBrush(Qt::blue);
    painter->drawRect(0, 0, width(), height());
}

void MyShadowItem::setShadowColor(const QColor &color)
{
    if (m_shadowColor != color) {
        m_shadowColor = color;
        emit shadowColorChanged();
        update(); // 再描画を要求
    }
}

void MyShadowItem::setShadowRadius(qreal radius)
{
    if (m_shadowRadius != radius) {
        m_shadowRadius = radius;
        emit shadowRadiusChanged();
        update(); // 再描画を要求
    }
}

QML での利用

import QtQuick 2.0
import MyCustomItems 1.0 // QMLに登録したモジュール名とバージョン

Rectangle {
    width: 400
    height: 300
    color: "#f0f0f0"

    MyShadowItem { // カスタムアイテムを使用
        width: 150
        height: 100
        anchors.centerIn: parent
        shadowColor: "black"
        shadowRadius: 10 // QMLからプロパティを制御
    }
}

利点

  • パフォーマンスがクリティカルな場合に最適化しやすい。
  • グラフィックスパイプラインを深く制御できる(特に QQuickItem のサブクラス化)。
  • 完全にカスタマイズされた描画ロジック。

欠点

  • QQuickPaintedItem はCPUレンダリングのため、GPUエフェクトのような複雑なぼかしやシェーダー効果を再現するのが難しい場合がある。
  • 実装が複雑になる傾向がある。
  • C++ と QML の両方の知識が必要。

これは最も低レベルな方法で、Qt Quick のシーングラフの概念を理解している必要があります。QQuickItem をサブクラス化し、updatePaintNode() メソッドをオーバーライドして、直接 QSGNode オブジェクト(形状、テクスチャ、マテリアルなど)を構築・更新します。

特徴

  • 非常に高いパフォーマンスが期待できる。
  • カスタムシェーダーをC++側で定義し、QMLアイテムに適用できる。
  • GPU上での描画を最大限に制御できる。

欠点

  • ほとんどのユースケースでは Item.layer.effectShaderEffect で十分。
  • OpenGL/Vulkan/Direct3D など、バックエンドグラフィックスAPIに関する知識が必須。
  • 学習曲線が急峻。

Item.layer.effect は、QMLアイテムにグラフィカルな効果を適用するための、宣言的で使いやすい、かつパフォーマンスも良好な優れた方法です。多くの場合、これで十分です。

しかし、以下のような場合は代替方法を検討する価値があります。

  • 非常に特殊な描画ロジックや最高のパフォーマンス
    C++ で QQuickPaintedItemQQuickItem をサブクラス化する。特に QQuickItem のサブクラス化は、シーングラフへの直接的な介入を意味するため、複雑なGPUベースのエフェクトをゼロから構築する場合にのみ推奨されます。
  • ソースの再利用や詳細なテクスチャ制御
    ShaderEffectSource を直接使用する。
  • シンプルなアニメーションやプロパティ変更
    Item.layer.effect のオーバーヘッドが不要な場合は、直接QMLプロパティとアニメーションを使う。