QOpenGLWidget Class
QOpenGLWidget クラスは、OpenGL グラフィックスをレンダリングするためのウィジェットです。詳細...
| ヘッダー: | #include <qopenglwidget.h> |
| CMake: | find_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets) target_link_libraries(mytarget PRIVATE Qt6::OpenGLWidgets) |
| qmake: | QT += openglwidgets |
| 継承元: | QWidget |
パブリック型
(since 6.5) enum | TargetBuffer { LeftBuffer, RightBuffer } |
| enum | UpdateBehavior { NoPartialUpdate, PartialUpdate } |
パブリック関数
| QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()) | |
| virtual | ~QOpenGLWidget() |
| QOpenGLContext * | context() const |
(since 6.5) QOpenGLWidget::TargetBuffer | currentTargetBuffer() const |
| GLuint | defaultFramebufferObject() const |
(since 6.5) GLuint | defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const |
| void | doneCurrent() |
| QSurfaceFormat | format() const |
| QImage | grabFramebuffer() |
(since 6.5) QImage | grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer) |
| bool | isValid() const |
| void | makeCurrent() |
(since 6.5) void | makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer) |
| void | setFormat(const QSurfaceFormat &format) |
| void | setTextureFormat(GLenum texFormat) |
| void | setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior) |
| GLenum | textureFormat() const |
| QOpenGLWidget::UpdateBehavior | updateBehavior() const |
シグナル
| void | aboutToCompose() |
| void | aboutToResize() |
| void | frameSwapped() |
| void | resized() |
保護された関数
| virtual void | initializeGL() |
| virtual void | paintGL() |
| virtual void | resizeGL(int w, int h) |
再実装された保護関数
| virtual bool | event(QEvent *e) override |
| virtual int | metric(QPaintDevice::PaintDeviceMetric metric) const override |
| virtual QPaintEngine * | paintEngine() const override |
| virtual void | paintEvent(QPaintEvent *e) override |
| virtual QPaintDevice * | redirected(QPoint *p) const override |
| virtual void | resizeEvent(QResizeEvent *e) override |
詳細な説明
Qt OpenGL Widgetは、Qtアプリケーションに統合されたOpenGLグラフィックスを表示するための機能を提供します。使い方は非常に簡単です。クラスをこのクラスから継承させ、他のQWidget と同様にサブクラスを使用するだけです。ただし、QPainter と標準のOpenGLレンダリングコマンドのどちらを使用するかを選択できる点が異なります。
QOpenGLWidget は、サブクラスで再実装して一般的な OpenGL タスクを実行できる、3 つの便利な仮想関数を提供しています:
- paintGL() - OpenGLシーンをレンダリングします。ウィジェットの更新が必要なたびに呼び出されます。
- resizeGL() - OpenGLのビューポートや投影設定などを設定します。ウィジェットのサイズが変更されるたびに呼び出されます(また、新しく作成されたウィジェットはすべて自動的にサイズ変更イベントを受け取るため、初めて表示される際にも呼び出されます)。
- initializeGL() - OpenGLのリソースと状態を設定します。resizeGL()またはpaintGL()が最初に呼び出される前に一度呼び出されます。
paintGL() 以外の場所から再描画をトリガーする必要がある場合(典型的な例は、timers を使用してシーンをアニメーション化する場合です)、ウィジェットのupdate() 関数を呼び出して更新をスケジュールする必要があります。
paintGL()、resizeGL()、またはinitializeGL() が呼び出されると、ウィジェットの OpenGL レンダリングコンテキストが現在のものになります。他の場所(例えば、ウィジェットのコンストラクタ内や独自のペイント関数内)から標準の OpenGL API 関数を呼び出す必要がある場合は、まずmakeCurrent() を呼び出す必要があります。
すべてのレンダリングは OpenGL フレームバッファオブジェクト内で行われます。makeCurrent() は、それがコンテキストにバインドされていることを保証します。paintGL() 内のレンダリングコードで追加のフレームバッファオブジェクトを作成およびバインドする際は、この点に留意してください。ID 0 のフレームバッファを再バインドしてはいけません。代わりに、defaultFramebufferObject() を呼び出して、バインドすべき ID を取得してください。
QOpenGLWidget では、プラットフォームがサポートしている場合、異なる OpenGL バージョンやプロファイルを使用することができます。setFormat() を通じて、希望するフォーマットを設定するだけです。ただし、同じウィンドウ内に複数の QOpenGLWidget インスタンスが存在する場合、それらはすべて同じフォーマットを使用するか、少なくともコンテキストの共有を妨げないフォーマットを使用する必要がある点に注意してください。この問題を回避するには、setFormat() の代わりにQSurfaceFormat::setDefaultFormat() を使用することを推奨します。
注: OpenGLコアプロファイルのコンテキストが要求される場合、一部のプラットフォーム(例:macOS)では、QApplication インスタンスを構築する前にQSurfaceFormat::setDefaultFormat()を呼び出すことが 必須です。これは、すべての内部コンテキストが正しいバージョンとプロファイルを使用して作成されるようにし、コンテキスト間のリソース共有が正常に機能し続けることを保証するためです。
描画手法
前述のように、純粋な 3D コンテンツをレンダリングするには、次のように QOpenGLWidget をサブクラス化します。
- initializeGL() およびresizeGL() 関数を再実装し、OpenGLの状態を設定して、遠近法変換を適用します。
- paintGL() を再実装し、OpenGL 関数のみを呼び出して 3D シーンを描画します。
また、QPainter を使用して、QOpenGLWidget のサブクラス上に 2D グラフィックスを描画することも可能です:
- paintGL() では、OpenGL コマンドを発行する代わりに、ウィジェット上で使用するQPainter オブジェクトを生成します。
- QPainter のメンバ関数を使用してプリミティブを描画します。
- 直接的な OpenGL コマンドの発行も引き続き可能です。ただし、これらのコマンドは、ペインターの beginNativePainting() および endNativePainting() の呼び出しで囲まれていることを確認する必要があります。
QPainter のみを使用して描画を行う場合、通常のウィジェットと同様に、paintEvent() を再実装することで描画を行うことも可能です。
- paintEvent() 関数を再実装します。
- そのウィジェットを対象とするQPainter オブジェクトを構築します。ウィジェットをコンストラクタまたはQPainter::begin() 関数に渡します。
- QPainter のメンバ関数を使用してプリミティブを描画します。
- 描画が完了すると、QPainter インスタンスは破棄されます。あるいは、QPainter::end()を明示的に呼び出すこともできます。
OpenGL 関数呼び出し、ヘッダー、および QOpenGLFunctions
OpenGL 関数を呼び出す際は、関数を直接呼び出すことを避けることを強く推奨します。代わりに、(移植性の高いアプリケーションを作成する場合は)QOpenGLFunctions を使用するか、あるいは(最新のデスクトップ専用 OpenGL をターゲットとする場合は、QOpenGLFunctions_3_2_Core や類似のバージョン指定されたバリアント)を使用することを推奨します。 こうすることで、動的な OpenGL 実装の読み込みを行う構成を含め、すべての Qt ビルド構成においてアプリケーションが正しく動作します。これは、アプリケーションが GL 実装に直接リンクしていないため、直接的な関数呼び出しが実行できないことを意味します。
paintGL() では、QOpenGLContext::currentContext() を呼び出すことで、常に現在のコンテキストにアクセスできます。このコンテキストから、QOpenGLContext::functions() を呼び出すことで、すでに初期化され、使用可能な状態にあるQOpenGLFunctions インスタンスを取得できます。すべての GL 呼び出しに接頭辞を付ける代わりに、QOpenGLFunctions を継承し、initializeGL() 内でQOpenGLFunctions::initializeOpenGLFunctions() を呼び出す方法もあります。
OpenGL のヘッダーについては、ほとんどの場合、GL.h のようなヘッダーを直接インクルードする必要はないことに注意してください。OpenGL 関連の Qt OpenGL ヘッダーは qopengl.h をインクルードしており、qopengl.h はシステムに適したヘッダーをインクルードします。 これは、OpenGL ES 3.x や 2.0 のヘッダー、利用可能な最高バージョンのヘッダー、あるいはシステムが提供する gl.h などである可能性があります。 さらに、OpenGL および OpenGL ES 両方の拡張ヘッダー(一部のシステムでは glext.h と呼ばれる)のコピーが Qt の一部として提供されています。これらは、可能なプラットフォームでは自動的にインクルードされます。つまり、ARB、EXT、OES 拡張からの定数や関数ポインタの typedef が自動的に利用可能になります。
コード例
まず始めに、最も単純な QOpenGLWidget のサブクラスは次のようなものになります:
class MyGLWidget : public QOpenGLWidget
{
public:
MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }
protected:
void initializeGL() override
{
// Set up the rendering context, load shaders and other resources, etc.:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
...
}
void resizeGL(int w, int h) override
{
// Update projection matrix and other size related settings:
m_projection.setToIdentity();
m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
...
}
void paintGL() override
{
// Draw the scene:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClear(GL_COLOR_BUFFER_BIT);
...
}
};あるいは、QOpenGLFunctions を継承することで、すべてのOpenGL呼び出しにプレフィックスを付ける手間を省くこともできます:
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
...
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(...);
...
}
...
};特定のOpenGLバージョンやプロファイルと互換性のあるコンテキストを取得したり、深度バッファやステンシルバッファを要求したりするには、setFormat()を呼び出します:
QOpenGLWidget *widget = new QOpenGLWidget(parent);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
widget->setFormat(format); // must be called before the widget or its parent window gets shown注: 基盤となるウィンドウシステムインターフェースから深度バッファおよびステンシルバッファを確実に要求するかどうかは 、アプリケーション側の責任です 。深度バッファのサイズをゼロ以外で要求しない限り、深度バッファが利用可能である保証はなく、その結果、深度テストに関連する OpenGL 操作が期待通りに機能しない可能性があります。 一般的に使用される深度バッファおよびステンシルバッファのサイズ要求値は、それぞれ 24 および 8 です。
OpenGL 3.0 以降のコンテキストにおいて、移植性が重要でない場合は、バージョン指定された `QOpenGLFunctions ` バリアントを使用することで、特定のバージョンで利用可能なすべての最新の OpenGL 関数に簡単にアクセスできます:
...
void paintGL() override
{
QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
...
f->glDrawArraysInstanced(...);
...
}
...前述のように、アプリケーションの実行期間中、すべてのウィンドウおよびコンテキストに適用されるよう、要求するフォーマットをグローバルに設定する方が、より簡潔で堅牢です。以下にその例を示します:
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
MyWidget widget;
widget.show();
return app.exec();
}マルチサンプリング
マルチサンプリングを有効にするには、setFormat() に渡されるQSurfaceFormat で、要求するサンプル数を設定します。マルチサンプリングをサポートしていないシステムでは、この要求は無視される場合があります。
マルチサンプリングをサポートするには、マルチサンプリングされたレンダリングバッファおよびフレームバッファのブリットがサポートされている必要があります。 OpenGL ES 2.0の実装では、これらがサポートされていない可能性が高いです。つまり、マルチサンプリングは利用できません。最新のOpenGLバージョンやOpenGL ES 3.0以降では、通常、これはもはや問題にはなりません。
スレッド処理
たとえば、paintGL() 内で GUI/メインスレッドで使用されるテクスチャを生成するなど、ワーカースレッド上でオフスクリーンレンダリングを実行する場合、ウィジェットの `QOpenGLContext ` を公開することで、各スレッド上でこれと共有する追加のコンテキストを作成できるようになり、これがサポートされます。
GUI/メインスレッド外で QOpenGLWidget のフレームバッファに直接描画するには、paintEvent() を何も行わないように再実装します。コンテキストのスレッドアフィニティは、QObject::moveToThread() を使用して変更する必要があります。その後、ワーカースレッド上でmakeCurrent() およびdoneCurrent() を使用できるようになります。 その後、コンテキストをGUIスレッドまたはメインスレッドに戻すよう注意してください。
QOpenGLWidget のみを対象としたバッファスワップをトリガーすることはできません。これは、QOpenGLWidget には実際の画面上のネイティブサーフェスが存在しないためです。GUI スレッド上での合成およびバッファスワップの管理は、ウィジェットスタックに委ねられています。スレッドがフレームバッファの更新を完了したら、GUI/メインスレッドで update() を呼び出し、合成をスケジュールしてください。
GUI/メインスレッドがコンポジティングを実行している間は、フレームバッファの使用を避けるよう特に注意する必要があります。コンポジティングの開始時および終了時には、aboutToCompose() およびframeSwapped() シグナルが発信されます。これらはGUI/メインスレッド上で発信されます。 つまり、直接接続を使用する場合、aboutToCompose() は、ワーカースレッドがレンダリングを完了するまで GUI/メインスレッドをブロックする可能性があります。その後、ワーカースレッドは、frameSwapped() シグナルが発信されるまで、それ以上のレンダリングを行ってはなりません。 これが許容できない場合は、ワーカースレッドでダブルバッファリングの仕組みを実装する必要があります。これには、スレッドによって完全に制御される代替のレンダリングターゲット(例えば、追加のフレームバッファオブジェクト)を使用して描画を行い、適切なタイミングで QOpenGLWidget のフレームバッファにブリット処理を行うことが含まれます。
コンテキストの共有
複数の QOpenGLWidget が同じ最上位ウィジェットの子として追加されると、それらのコンテキストは互いに共有されます。これは、異なるウィンドウに属する QOpenGLWidget インスタンスには適用されません。
つまり、同じウィンドウ内のすべての QOpenGLWidget は、テクスチャなどの共有可能なリソースに相互にアクセスでき、追加の「グローバル共有」コンテキストは必要ありません。
異なるウィンドウに属する QOpenGLWidget インスタンス間で共有を設定するには、QApplication をインスタンス化する前に、Qt::AA_ShareOpenGLContexts アプリケーション属性を設定してください。これにより、それ以上の手順を必要とせずに、すべての QOpenGLWidget インスタンス間で共有が有効になります。
QOpenGLWidgetのコンテキストとテクスチャなどのリソースを共有する、追加のQOpenGLContext インスタンスを作成することも可能です。QOpenGLContext::create()を呼び出す前に、context()から返されたポインタをQOpenGLContext::setShareContext()に渡すだけです。結果として得られるコンテキストは別のスレッドでも使用できるため、スレッド化されたテクスチャ生成や非同期のテクスチャアップロードが可能になります。
なお、QOpenGLWidget は、基盤となるグラフィックスドライバに関して、標準に準拠したリソース共有の実装を前提としています。例えば、一部のドライバ(特にモバイルや組み込みハードウェア向けのもの)では、既存のコンテキストと後で作成されるコンテキスト間の共有設定に問題が生じることがあります。また、異なるスレッド間で共有リソースを利用しようとすると、予期しない動作をするドライバもあります。
リソースの初期化とクリーンアップ
initializeGL() およびpaintGL() が呼び出される際は、QOpenGLWidget に関連付けられた OpenGL コンテキストが常に最新の状態であることが保証されています。initializeGL() が呼び出される前に OpenGL リソースを作成しようとしないでください。たとえば、サブクラスのコンストラクタ内でシェーダーのコンパイル、頂点バッファオブジェクトの初期化、またはテクスチャデータのアップロードを試みると、失敗します。 これらの操作は、initializeGL() まで延期する必要があります。QOpenGLBuffer やQOpenGLVertexArrayObject などの Qt OpenGL ヘルパークラスの一部は、これに対応する延期動作を持っています。つまり、コンテキストなしでインスタンス化できますが、すべての初期化はcreate() などの呼び出しが行われるまで延期されます。 つまり、これらは QOpenGLWidget のサブクラス内で通常の(ポインタではない)メンバ変数として使用できますが、create() や類似の関数は、initializeGL() からのみ呼び出すことができます。 ただし、すべてのクラスがこのように設計されているわけではない点に注意してください。判断に迷う場合は、メンバ変数をポインタ型にし、initializeGL() 内でインスタンスを動的に生成し、デストラクタ内で破棄するようにしてください。
リソースを解放する際も、コンテキストがアクティブな状態である必要があります。したがって、このようなクリーンアップを行うデストラクタでは、OpenGLリソースやラッパーを破棄する前に、makeCurrent() を呼び出すことが求められます。deleteLater() による遅延削除や、QObject の親子関係メカニズムによる削除は避けてください。当該インスタンスが実際に破棄される時点で、正しいコンテキストがアクティブな状態であるという保証はありません。
したがって、リソースの初期化と破棄に関して、典型的なサブクラスは多くの場合、次のような形になります:
class MyGLWidget : public QOpenGLWidget
{
...
private:
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLShaderProgram *m_program;
QOpenGLShader *m_shader;
QOpenGLTexture *m_texture;
};
MyGLWidget::MyGLWidget()
: m_program(0), m_shader(0), m_texture(0)
{
// No OpenGL resource initialization is done here.
}
MyGLWidget::~MyGLWidget()
{
// Make sure the context is current and then explicitly
// destroy all underlying OpenGL resources.
makeCurrent();
delete m_texture;
delete m_shader;
delete m_program;
m_vbo.destroy();
m_vao.destroy();
doneCurrent();
}
void MyGLWidget::initializeGL()
{
m_vao.create();
if (m_vao.isCreated())
m_vao.bind();
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(...);
m_texture = new QOpenGLTexture(QImage(...));
m_shader = new QOpenGLShader(...);
m_program = new QOpenGLShaderProgram(...);
...
}これはほとんどの場合に機能しますが、汎用的な解決策としては完全には理想的ではありません。ウィジェットが再親付けされ、まったく異なるトップレベルウィンドウに配置される場合、さらなる対策が必要になります。QOpenGLContext のaboutToBeDestroyed()シグナルに接続することで、OpenGLコンテキストが解放されようとするたびにクリーンアップを実行できるようになります。
注: ライフサイクル中に関連付けられているトップレベルウィンドウを複数回変更するウィジェットの場合 、以下のコードスニペットで示されているような、組み合わせたクリーンアップ手法が不可欠です。ウィジェットまたはその親が再配置され、トップレベルウィンドウが変更されるたびに、ウィジェットに関連付けられたコンテキストは破棄され、新しいものが作成されます。 その後、initializeGL() が呼び出され、すべての OpenGL リソースが再初期化される必要があります。このため、適切なクリーンアップを行う唯一の方法は、コンテキストの aboutToBeDestroyed() シグナルに接続することです。シグナルが発信された時点で、対象のコンテキストが現在のものとは限らない点に注意してください。 したがって、接続されたスロット内で `makeCurrent()` を呼び出すことが推奨されます。さらに、ウィジェットが破棄される際にシグナルに接続されたスロットやラムダが呼び出されない可能性があるため、派生クラスのデストラクタからも同じクリーンアップ手順を実行する必要があります。
MyGLWidget::~MyGLWidget()
{
cleanup();
}
void MyGLWidget::initializeGL()
{
...
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}
void MyGLWidget::cleanup()
{
makeCurrent();
delete m_texture;
m_texture = 0;
...
doneCurrent();
disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}注: Qt::AA_ShareOpenGLContexts が設定されている場合 、ウィジェットのコンテキストは、親の変更時であっても決して変更されません。これは、ウィジェットに関連付けられたテクスチャが、新しいトップレベルコンテキストからもアクセス可能になるためです。したがって、このフラグが設定されている場合、コンテキストのaboutToBeDestroyed()シグナルに対応することは必須ではありません。
コンテキストの共有があるため、適切なクリーンアップが特に重要です。各 QOpenGLWidget に関連付けられたコンテキストは QOpenGLWidget とともに破棄されますが、そのコンテキスト内のテクスチャなどの共有可能なリソースは、QOpenGLWidget が存在していた最上位ウィンドウが破棄されるまで有効なままとなります。 さらに、Qt::AA_ShareOpenGLContexts などの設定や一部のQtモジュールにより、コンテキストの共有範囲がさらに広くなる可能性があり、その結果、当該リソースがアプリケーションの存続期間全体にわたって保持され続ける恐れがあります。したがって、最も安全かつ堅牢な方法は、QOpenGLWidgetで使用されたすべてのリソースおよびリソースラッパーに対して、常に明示的なクリーンアップを行うことです。
制限事項およびその他の考慮事項
他のウィジェットを QOpenGLWidget の下に配置し、QOpenGLWidget を透明にしても、期待通りの結果は得られません。つまり、下にあるウィジェットは表示されません。これは、実際には QOpenGLWidget が他のすべての通常の(OpenGL 以外の)ウィジェットよりも先に描画されるためであり、したがって、透過させるような解決策は実現不可能です。 QOpenGLWidgetの上にウィジェットを配置するなどの、他の種類のレイアウトは期待通りに機能します。
どうしても必要な場合は、QOpenGLWidget のQt::WA_AlwaysStackOnTop 属性を設定することで、この制限を回避することができます。 ただし、これにより重ね合わせ順序が破られることに注意してください。例えば、QOpenGLWidgetの上に他のウィジェットを配置することはできなくなるため、この設定は、半透明のQOpenGLWidgetの下に他のウィジェットを表示する必要がある場合にのみ使用すべきです。
なお、下に他のウィジェットがなく、半透明のウィンドウにすることを意図している場合は、この制限は適用されません。その場合は、最上位のウィンドウにQt::WA_TranslucentBackground を設定するという従来の手法で十分です。 なお、QOpenGLWidget内でのみ透過領域が必要な場合は、Qt::WA_TranslucentBackground を有効にした後、Qt::WA_NoSystemBackground をfalse に戻す必要があります。また、システムによっては、setFormat() を通じてQOpenGLWidgetのコンテキストに対してアルファチャネルを要求する必要がある場合もあります。
QOpenGLWidgetは、QOpenGLWindow と同様に、複数の更新挙動をサポートしています。preservedモードでは、前回のpaintGL()呼び出しでレンダリングされた内容が次の呼び出しでも利用可能となり、増分レンダリングが可能になります。non-preservedモードでは、その内容は失われ、paintGL()の実装ではビュー内のすべてを再描画することが求められます。
Qt 5.5 以前、QOpenGLWidget のデフォルトの動作は、paintGL() の呼び出し間でレンダリングされた内容を保持することでした。Qt 5.5 以降、パフォーマンスが向上し、また大多数のアプリケーションでは以前のコンテンツを必要としないため、デフォルトの動作は非保持モードに変更されました。 これは、OpenGLベースのQWindow のセマンティクスにも似ており、各フレームごとに色バッファや補助バッファが無効化されるという点で、QOpenGLWindow のデフォルトの挙動とも一致しています。保存された挙動を復元するには、PartialUpdate を指定してsetUpdateBehavior()を呼び出してください。
注: QOpenGLWidget をウィジェット階層に動的に追加する場合 (例えば、対応する最上位ウィジェットがすでに画面上に表示されているウィジェットに、新しい QOpenGLWidget を子として追加する場合など)、その QOpenGLWidget がそのウィンドウ内で同種の最初のインスタンスである場合、関連するネイティブウィンドウが暗黙的に破棄され、再作成されることがあります。 これは、ウィンドウのタイプが `RasterSurface ` から `OpenGLSurface ` に変更され、それがプラットフォーム固有の影響をもたらすためです。この動作は Qt 6.4 で新たに導入されました。
QOpenGLWidgetがウィジェット階層に追加されると、トップレベルウィンドウの内容はOpenGLベースのレンダリングによってフラッシュされます。QOpenGLWidget以外のウィジェットは、ソフトウェアベースのペインターを使用して引き続きコンテンツを描画しますが、最終的な合成は3D APIを通じて行われます。
注: QOpenGLWidget を表示するには 、他の `QWidget` ベースのコンテンツとの合成の仕組み上、関連する最上位ウィンドウのバッキングストアにアルファチャンネルが必要です。 アルファチャンネルがない場合、QOpenGLWidget によってレンダリングされたコンテンツは表示されません。これは、Linux/X11 でのリモートディスプレイ環境(Xvnc など)において、色深度が 24 未満の場合に特に問題となる可能性があります。 たとえば、色深度が 16 の場合、通常はQImage::Format_RGB16 (RGB565)形式のバッキングストア画像が使用されるため、アルファチャンネル用の領域が残されません。 したがって、QOpenGLWidget の内容がウィンドウ内の他のウィジェットと正しく合成されないという問題が発生した場合は、サーバー(vncserver など)が 16 ビット深度ではなく、24 ビットまたは 32 ビット深度に設定されていることを確認してください。
代替手段
ウィンドウに QOpenGLWidget を追加すると、ウィンドウ全体で OpenGL ベースの合成が有効になります。特殊なケースでは、これが理想的ではない場合もあり、独立したネイティブの子ウィンドウを使用する従来の QGLWidget スタイルの挙動が望まれることもあります。 このアプローチの制限事項(例えば、オーバーラップ、透明度、スクロールビュー、MDI領域など)を理解しているデスクトップアプリケーションでは、`QOpenGLWindow ` と `QWidget::createWindowContainer()` を使用できます。これは QGLWidget の現代的な代替手段であり、追加の合成ステップが不要なため、QOpenGLWidget よりも高速です。 このアプローチの使用は、他に選択肢がない場合に限定することを強く推奨します。なお、このオプションはほとんどの組み込みおよびモバイルプラットフォームには適しておらず、特定のデスクトッププラットフォーム(例:macOS)でも問題が発生することが知られています。安定したクロスプラットフォームのソリューションは、常に QOpenGLWidget です。
立体視レンダリング
バージョン 6.5 以降、QOpenGLWidget は立体レンダリングをサポートしています。これを有効にするには、ウィンドウの作成前に QSurfaceFormat::SetDefaultFormat() を使用して、QSurfaceFormat::StereoBuffers フラグをグローバルに設定してください。
注: フラグの内部的な処理の仕組み上、setFormat()を使用しても 必ずしも機能するとは限りません。
これにより、各フレームごとにpaintGL() が 2 回呼び出され、QOpenGLWidget::TargetBuffer ごとに 1 回ずつ実行されます。paintGL() 内で、currentTargetBuffer() を呼び出して、現在描画が行われているバッファを照会してください。
注: 左右のカラーバッファをより細かく制御したい場合は 、代わりに `QOpenGLWindow ` と `QWidget::createWindowContainer()` を併用することを検討してください。
注:この 種の 3D レンダリングには、グラフィックカードにステレオ対応の設定が必要であるなど、特定のハードウェア要件があります。
OpenGL は、米国およびその他の国における Silicon Graphics, Inc. の商標です。
QOpenGLFunctions 、QOpenGLWindow 、Qt::AA_ShareOpenGLContexts 、およびUpdateBehaviorも参照してください 。
メンバ型のドキュメント
[since 6.5] enum QOpenGLWidget::TargetBuffer
QSurfaceFormat::StereoBuffers の設定によってオン/オフが切り替わるステレオレンダリングが有効な場合に使用するバッファを指定します。
注:LeftBuffer は 常にデフォルトであり、ステレオレンダリングが無効になっている場合や、グラフィックスドライバがステレオレンダリングをサポートしていない場合のフォールバック値として使用されます。
| 定数 | 値 |
|---|---|
QOpenGLWidget::LeftBuffer | 0 |
QOpenGLWidget::RightBuffer | 1 |
この列挙型は Qt 6.5 で導入されました。
enum QOpenGLWidget::UpdateBehavior
この列挙型は、QOpenGLWidget の更新セマンティクスを記述するものです。
| 定数 | 値 | 説明 |
|---|---|---|
QOpenGLWidget::NoPartialUpdate | 0 | QOpenGLWidget は、QOpenGLWidget が画面にレンダリングされた後、カラーバッファおよび補助バッファの内容を破棄します。これは、デフォルトのopenglが有効なQWindow を引数としてQOpenGLContext::swapBuffers を呼び出した場合に期待される動作と同じです。 NoPartialUpdate は、フレームバッファオブジェクトがレンダリングターゲットとして使用されている場合、モバイルや組み込み分野で一般的な特定のハードウェアアーキテクチャにおいて、パフォーマンス上のメリットをもたらすことがあります。 フレームバッファオブジェクトは、フレーム間で glInvalidateFramebuffer(サポートされている場合)、あるいはフォールバックとして glDiscardFramebufferEXT(サポートされている場合)または glClear の呼び出しによって無効化されます。 |
QOpenGLWidget::PartialUpdate | 1 | フレームバッファオブジェクトのカラーバッファおよび補助バッファは、フレーム間で無効化されません。 |
updateBehavior() およびsetUpdateBehavior()も参照してください 。
メンバ関数のドキュメント
[explicit] QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
parent の子となるウィジェットを作成し、ウィジェットのフラグをf に設定します。
[virtual noexcept] QOpenGLWidget::~QOpenGLWidget()
QOpenGLWidget インスタンスを破棄し、そのリソースを解放します。
デストラクタ内で、QOpenGLWidget のコンテキストが現在のコンテキストとして設定されるため、このウィジェットが提供するコンテキストに属する OpenGL リソースを解放する必要がある子オブジェクトを安全に破棄することができます。
警告: OpenGLWidget のサブクラスに、OpenGL リソース(QOpenGLBuffer 、QOpenGLShaderProgram など)をラップするオブジェクトをメンバとして持つ場合 、そのサブクラスのデストラクタにもmakeCurrent() の呼び出しを追加する必要がある場合があります。 C++のオブジェクト破棄のルールにより、これらのオブジェクトは本関数が呼び出される前に破棄されます(ただし、その後にサブクラスのデストラクタが実行されます)。そのため、本関数内でOpenGLコンテキストを現在のものにする処理が行われるのは、これらのオブジェクトを安全に破棄するには遅すぎます。
makeCurrentも参照してください 。
[signal] void QOpenGLWidget::aboutToCompose()
このシグナルは、ウィジェットの最上位ウィンドウが、そのQOpenGLWidget の子要素やその他のウィジェットのテクスチャの合成を開始しようとしているときに発火します。
[signal] void QOpenGLWidget::aboutToResize()
このシグナルは、ウィジェットのサイズが変更され、その結果、フレームバッファオブジェクトが再作成されようとしているときに発火します。
QOpenGLContext *QOpenGLWidget::context() const
このウィジェットで使用されているQOpenGLContext を返します。まだ初期化されていない場合は、0 を返します。
注: setParent() を使用してウィジェットの親オブジェクトを変更すると、ウィジェットが使用するコンテキストおよびフレームバッファオブジェクトが 変更されます。
関連項目: QOpenGLContext::setShareContext() およびdefaultFramebufferObject()。
[since 6.5] QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const
現在アクティブなターゲットバッファを返します。デフォルトでは左バッファが返されますが、右バッファはQSurfaceFormat::StereoBuffers が有効になっている場合にのみ使用されます。ステレオレンダリングが有効になっている場合、paintGL() を呼び出すことで、現在どのバッファが使用されているかを確認できます。paintGL() は、ターゲットごとに1回ずつ、計2回呼び出されます。
この関数は Qt 6.5 で導入されました。
paintGL()も参照してください 。
GLuint QOpenGLWidget::defaultFramebufferObject() const
フレームバッファオブジェクトのハンドルを返します。まだ初期化されていない場合は、0 を返します。
注: フレームバッファオブジェクトは 、context() によって返されるコンテキストに属しており、他のコンテキストからはアクセスできない場合があります。
注: setParent() を使用してウィジェットの親コンテキストを変更すると、そのウィジェットが使用するコンテキスト およびフレームバッファオブジェクトが 変更されます。また、サイズ変更のたびにフレームバッファオブジェクトが変更されます。
context()も参照してください 。
[since 6.5] GLuint QOpenGLWidget::defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const
指定されたターゲットバッファのフレームバッファオブジェクトハンドルを返します。まだ初期化されていない場合は、0 を返します。
このオーバーロードを呼び出す意味があるのは、QSurfaceFormat::StereoBuffers が有効であり、かつハードウェアでサポートされている場合のみです。そうでない場合、このメソッドはデフォルトのバッファを返します。
注: フレームバッファオブジェクトは 、context() によって返されるコンテキストに属しており、他のコンテキストからはアクセスできない場合があります。setParent() を使用してウィジェットの親を変更すると、ウィジェットが使用するコンテキストおよびフレームバッファオブジェクトが変更されます。また、フレームバッファオブジェクトはリサイズが行われるたびに変更されます。
この関数は Qt 6.5 で導入されました。
context()も参照してください 。
void QOpenGLWidget::doneCurrent()
コンテキストを解放します。
paintGL() を呼び出す際、ウィジェットがコンテキストのバインドと解放を適切に処理するため、ほとんどの場合、この関数を呼び出す必要はありません。
[override virtual protected] bool QOpenGLWidget::event(QEvent *e)
QWidget::event(QEvent *event) を再実装しています。
QSurfaceFormat QOpenGLWidget::format() const
このウィジェットとそのトップレベルウィンドウで使用されているコンテキストおよびサーフェス形式を返します。
ウィジェットとそのトップレベルウィンドウの両方が作成、サイズ変更、表示された後、この関数はコンテキストの実際のフォーマットを返します。プラットフォームによって要求が満たされなかった場合、これは要求されたフォーマットとは異なる可能性があります。また、要求したサイズよりも大きなカラーバッファサイズが割り当てられることもあります。
ウィジェットのウィンドウおよび関連する OpenGL リソースがまだ初期化されていない場合、戻り値はsetFormat() を通じて設定されたフォーマットになります。
setFormat() およびcontext()も参照してください 。
[signal] void QOpenGLWidget::frameSwapped()
このシグナルは、ウィジェットの最上位ウィンドウのコンポジションが完了し、ブロックされる可能性のあるQOpenGLContext::swapBuffers()呼び出しから戻った後に発火します。
QImage QOpenGLWidget::grabFramebuffer()
フレームバッファの32ビットRGB画像をレンダリングして返します。
注:この処理は 、ピクセルの読み取りに glReadPixels() を使用するため、処理負荷が高くなる可能性があります。処理が遅くなり、GPU パイプラインが停滞する恐れがあります。
[since 6.5] QImage QOpenGLWidget::grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)
指定されたターゲットバッファのフレームバッファを32ビットRGB画像としてレンダリングし、返します。このオーバーロードは、QSurfaceFormat::StereoBuffers が有効になっている場合にのみ呼び出す意味があります。適切なターゲットバッファのフレームバッファを取得しても、ステレオレンダリングが無効になっている場合や、ハードウェアでサポートされていない場合は、デフォルトの画像が返されます。
注:この操作は 、ピクセルの読み取りに glReadPixels() を使用するため、処理コストが高くなる可能性があります。処理が遅くなり、GPU パイプラインが停滞する恐れがあります。
この関数は Qt 6.5 で導入されました。
[virtual protected] void QOpenGLWidget::initializeGL()
この仮想関数は、paintGL() またはresizeGL() が最初に呼び出される前に 1 回呼び出されます。サブクラスでこれを再実装してください。
この関数では、必要な OpenGL リソースをすべて初期化する必要があります。
makeCurrent() を呼び出す必要はありません。この関数が呼び出された時点で、すでにその処理は完了しているからです。ただし、この段階ではフレームバッファはまだ利用できないため、ここから描画呼び出しを行わないでください。そのような呼び出しは、代わりにpaintGL() まで延期してください。
paintGL() およびresizeGL()も参照してください 。
bool QOpenGLWidget::isValid() const
ウィジェットおよびコンテキストなどの OpenGL リソースの初期化が正常に完了した場合、true を返します。なお、ウィジェットが表示されるまでは、戻り値は常に false となることに注意してください。
void QOpenGLWidget::makeCurrent()
このウィジェットの OpenGL コンテンツをレンダリングする準備として、対応するコンテキストをアクティブにし、そのコンテキスト内のフレームバッファオブジェクトをバインドします。
paintGL() を呼び出す前にこの関数が自動的に呼び出されるため、ほとんどの場合、この関数を明示的に呼び出す必要はありません。
context()、paintGL()、およびdoneCurrent()も参照してください 。
[since 6.5] void QOpenGLWidget::makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)
渡されたバッファのコンテキストをアクティブにし、そのコンテキスト内のフレームバッファオブジェクトをバインドすることで、このウィジェットの OpenGL コンテンツのレンダリング準備を行います。
注:この関数を 呼び出す意味があるのは 、ステレオレンダリングが有効になっている場合のみです。ステレオレンダリングが無効な状態で右バッファが要求されても、何も起こりません。
paintGL() を呼び出す前に自動的に呼び出されるため、ほとんどの場合、この関数を明示的に呼び出す必要はありません。
この関数は Qt 6.5 で導入されました。
関連項目: context()、paintGL()、およびdoneCurrent()。
[override virtual protected] int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
QWidget::metric (QPaintDevice::PaintDeviceMetric m)const を再実装します。
[override virtual protected] QPaintEngine *QOpenGLWidget::paintEngine() const
QWidget::paintEngine() const を再実装します。
[override virtual protected] void QOpenGLWidget::paintEvent(QPaintEvent *e)
QWidget::paintEvent (QPaintEvent *event)を再実装します。
ペイントイベントを処理します。
QWidget::update() を呼び出すと、ペイントイベントe が送信され、その結果、この関数が呼び出されます。(注:これは非同期であり、update() から戻った後のいずれかの時点で発生します。) その後、この関数は、いくつかの準備を行った後、仮想関数paintGL() を呼び出して、QOpenGLWidget のフレームバッファの内容を更新します。 その後、ウィジェットの最上位ウィンドウが、フレームバッファのテクスチャをウィンドウの他の部分と合成します。
[virtual protected] void QOpenGLWidget::paintGL()
この仮想関数は、ウィジェットの描画が必要なたびに呼び出されます。サブクラスでこれを再実装してください。
makeCurrent() を呼び出す必要はありません。この関数が呼び出される時点で、すでにその処理は行われているためです。
この関数を呼び出す前に、コンテキストとフレームバッファがバインドされ、glViewport() の呼び出しによってビューポートが設定されます。それ以外の状態は設定されず、フレームワークによるクリアや描画は行われません。
デフォルトの実装では glClear() が実行されます。サブクラスは基底クラスの実装を呼び出すことは想定されておらず、独自にクリア処理を行う必要があります。
注: 移植性を確保するため 、initializeGL() で設定された状態が保持されるとは期待しないでください。 その代わりに、paintGL()内で、例えばglEnable()を呼び出すなどして、必要な状態をすべて設定してください。これは、WebGLを搭載したWebAssemblyなどの一部のプラットフォームでは、状況によってはOpenGLコンテキストに制限があり、QOpenGLWidget で使用されたコンテキストが他の目的にも使用される可能性があるためです。
QSurfaceFormat::StereoBuffers が有効になっている場合、この関数は2回呼び出されます(各バッファごとに1回ずつ)。currentTargetBuffer()を呼び出すことで、現在どのバッファがバインドされているかを照会できます。
注: ハードウェアが立体表示に対応していない場合でも、各ターゲットのフレーム バッファへの 描画は 行われます。ウィンドウ内で実際に表示されるのは左側のバッファのみです。
initializeGL()、resizeGL()、およびcurrentTargetBuffer()も参照してください 。
[override virtual protected] QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)
QWidget::resizeEvent(QResizeEvent *event) を再実装します。
e のイベントパラメータとして渡されるリサイズイベントを処理します。仮想関数resizeGL()を呼び出します。
注: 派生クラスでこの関数をオーバーライドすることは避けてください 。やむを得ずオーバーライドする場合は、QOpenGLWidget の実装も確実に呼び出されるようにしてください。そうしないと、基盤となるフレームバッファオブジェクトおよび関連リソースのサイズ変更が適切に行われず、レンダリングが不正になる可能性があります。
[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)
この仮想関数は、ウィジェットのサイズが変更されるたびに呼び出されます。サブクラスでこれを再実装してください。新しいサイズは、w およびh に渡されます。
makeCurrent() を呼び出す必要はありません。この関数が呼び出される時点で、すでに呼び出されているためです。さらに、フレームバッファもバインドされています。
initializeGL() およびpaintGL()も参照してください 。
[signal] void QOpenGLWidget::resized()
このシグナルは、ウィジェットのサイズ変更に伴いフレームバッファオブジェクトが再作成された直後に発火します。
void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
指定されたサーフェスformat を設定します。
この関数を通じてフォーマットが明示的に設定されていない場合、QSurfaceFormat::defaultFormat() によって返されるフォーマットが使用されます。つまり、複数の OpenGL ウィジェットが存在する場合、最初のウィジェットを作成する前に、この関数への個別の呼び出しをQSurfaceFormat::setDefaultFormat() への単一の呼び出しに置き換えることができます。
注: この関数でアルファバッファを要求しても 、その下に他のウィジェットを表示させたい場合には、期待通りの結果が得られません。代わりに、Qt::WA_AlwaysStackOnTop を使用して、その下に他のウィジェットが見える半透明のQOpenGLWidget インスタンスを有効にしてください。ただし、これにより重ね合わせ順序が破られるため、QOpenGLWidget の上に他のウィジェットを配置することはできなくなる点に注意してください。
format()、Qt::WA_AlwaysStackOnTop 、およびQSurfaceFormat::setDefaultFormat()も参照してください 。
void QOpenGLWidget::setTextureFormat(GLenum texFormat)
texFormat のカスタム内部テクスチャ形式を設定します。
sRGBフレームバッファを使用する場合、GL_SRGB8_ALPHA8 のようなフォーマットを指定する必要があります。これは、この関数を呼び出すことで実現できます。
注: ウィジェットがすでに表示され、初期化が完了した後にこの関数を 呼び出しても、何の効果もありません。
注: 通常、この関数は 、色空間を `QColorSpace::SRgb` に設定する `QSurfaceFormat::setColorSpace()` の呼び出しと組み合わせて使用する必要があります。
「textureFormat()」も参照してください 。
void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)
このウィジェットの更新動作を `updateBehavior` に設定します。
updateBehavior()も参照してください 。
GLenum QOpenGLWidget::textureFormat() const
ウィジェットがすでに初期化されている場合はアクティブな内部テクスチャ形式を、形式が設定されているがウィジェットがまだ表示されていない場合は指定された形式を、setTextureFormat() が呼び出されておらずウィジェットがまだ表示されていない場合はnullptr を返します。
setTextureFormat()も参照してください 。
QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
ウィジェットの更新動作を返します。
setUpdateBehavior()も参照してください 。
© 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.