Item.layer.effect
通常、QMLのアイテムは、それが属するウィンドウに直接描画されます。しかし、layer.enabled: true
を設定することで、そのアイテムとその子孫全体をオフスクリーンサーフェス(テクスチャ)に描画させることができます。このオフスクリーンサーフェスは、その後ウィンドウに描画されます。
layer.effect
プロパティは、このオフスクリーンサーフェスに対して、さらに追加の視覚効果を適用するために使用されます。具体的には、ShaderEffect
型のコンポーネントを指定することが一般的です。
-
レイヤーの有効化(
layer.enabled
):layer.effect
を使うためには、まずlayer.enabled: true
を設定して、アイテムをオフスクリーンレンダリングするようにする必要があります。これにより、アイテムとその子要素が単一のテクスチャとして扱われ、そのテクスチャに対してエフェクトが適用されます。 -
ShaderEffectとの組み合わせ:
layer.effect
の最も一般的な使用方法は、ShaderEffect
型のアイテムを割り当てることです。ShaderEffect
は、カスタムのOpenGLシェーダー(バーテックスシェーダーとフラグメントシェーダー)を適用することで、ドロップシャドウ、ブラー(ぼかし)、色付け、ページめくりなどの様々な視覚効果をQMLで直接記述することを可能にします。ShaderEffect
に渡されるテクスチャは、layer.enabled
によって生成されたアイテムのオフスクリーンレンダリング結果となります。- シェーダー内で、このテクスチャを
sampler2D
型のuniform変数として受け取り、カスタムの描画ロジックを実装します。
-
パフォーマンスとメモリ:
layer.enabled
をtrue
に設定すると、そのアイテムの幅×高さ×4バイト(RGBA)のメモリがGPU上に確保されます。大きなアイテムにレイヤーを適用する場合は、メモリ使用量に注意が必要です。- レイヤーを使用するアイテムは、レンダリング時にバッチ処理されないため、多数のレイヤー化されたアイテムがあるシーンではパフォーマンスの問題が発生する可能性があります。
- そのため、通常は視覚効果が必要な間だけ
layer.enabled
をtrue
にし、効果が不要になったらfalse
に戻すことが推奨されます。
-
使用例:
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 モジュールのインポート忘れ
DropShadow
やGaussianBlur
など、Qt Quick に含まれる標準のエフェクトを使用する場合、QMLファイルにimport QtGraphicalEffects 1.0
(または適切なバージョン) を記述しているか確認してください。 - layer.enabled: false または未設定
layer.effect
を使用するには、必ずlayer.enabled: true
を設定し、アイテムをオフスクリーンレンダリングするようにする必要があります。これが最も基本的な要件です。
トラブルシューティング
clip: true
が問題の原因ではないか確認する。- エフェクトアイテムの
width
とheight
、および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.enabled
をtrue
に設定し、効果が不要になったら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
の中に配置された Rectangle
と Text
がオフスクリーンテクスチャに描画され、そのテクスチャ全体に対して 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.effect
にDropShadow
を割り当て、影の色、位置、ぼかし具合などを設定しています。myBox
のlayer.enabled: true
が、そのアイテム全体(Rectangle
とText
)を単一の画像としてオフスクリーンバッファに描画します。
例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 変数です。この変数は自動的に設定されます。ShaderEffect
のfragmentShader
プロパティに、GLSLのフラグメントシェーダーコードを直接記述しています。
- Qt 6.5以降の MultiEffect
Qt 6.5 以降では、QtQuick.Effects
モジュールのMultiEffect
が導入され、複数のエフェクトをより効率的に適用できるようになりました。可能であればこちらを利用することを検討してください。 - QtGraphicalEffects モジュール
DropShadow
やGaussianBlur
など、Qt Quick に組み込まれているグラフィカルエフェクトは、QtGraphicalEffects
モジュールに属しています。これらを使用するには、QMLファイルでimport QtGraphicalEffects 1.0
(または適切なバージョン) を行う必要があります。 - メモリ使用量
オフスクリーンテクスチャはGPUメモリを消費します。解像度の高いアイテムや多数のレイヤー化されたアイテムは、多くのメモリを必要とします。 - パフォーマンス
layer.enabled: true
はオフスクリーンレンダリングを行うため、特に大きなアイテムや多数のアイテムに適用すると、パフォーマンスに影響を与える可能性があります。必要な場合にのみ使用し、不要になったらlayer.enabled: false
に戻すことを検討してください。
Item.layer.effect の内部実装: ShaderEffectSource を直接利用する
Item.layer.enabled
は、実際には内部的に ShaderEffectSource
を使用してアイテムのレンダリング結果をテクスチャとして取得しています。したがって、より細かく制御したい場合や、複数のシェーダーに同じソースを供給したい場合などは、ShaderEffectSource
を直接使用することができます。
特徴
sourceRect
やtextureSize
など、レイヤーテクスチャの生成に関するより詳細な制御が可能です。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 の標準プロパティと Animation
や Transition
を直接使用する方が効率的です。
特徴
- 基本的な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.effect
やShaderEffect
で十分。 - OpenGL/Vulkan/Direct3D など、バックエンドグラフィックスAPIに関する知識が必須。
- 学習曲線が急峻。
Item.layer.effect
は、QMLアイテムにグラフィカルな効果を適用するための、宣言的で使いやすい、かつパフォーマンスも良好な優れた方法です。多くの場合、これで十分です。
しかし、以下のような場合は代替方法を検討する価値があります。
- 非常に特殊な描画ロジックや最高のパフォーマンス
C++ でQQuickPaintedItem
やQQuickItem
をサブクラス化する。特にQQuickItem
のサブクラス化は、シーングラフへの直接的な介入を意味するため、複雑なGPUベースのエフェクトをゼロから構築する場合にのみ推奨されます。 - ソースの再利用や詳細なテクスチャ制御
ShaderEffectSource
を直接使用する。 - シンプルなアニメーションやプロパティ変更
Item.layer.effect
のオーバーヘッドが不要な場合は、直接QMLプロパティとアニメーションを使う。