Detailed Description
QOpenGLWidget類是用於渲染OpenGL圖形。
除了可以選擇使用QPainter和標准的OpenGL渲染圖形,QOpenGLWidget類提供了在Qt應用程序中顯示OpenGL圖形的功能。它使用起來非常簡單:新建類繼承於QOpenGLWidget,使用方法就像繼承於QWidget類子類一樣。
QOpenGLWidget類提供了三個方便的虛函數,可以在新建的子類中重新實現以完成OpenGL的任務:
- paintGL()—渲染OpenGL場景,需要更新Widget時就會調用。
- resizeGL()—設置OpenGL視口,投影等。每當調整Widget的大小時(第一次顯示窗口Widget時會調用它,因為所有新創建Widget都會自動獲得調整大小的事件)。
- initializeGL()—建立OpenGL的資源和狀態。在第一次調用resizeGL()或paintGL()之前調用一次。
如果需要從paintGL()以外的地方觸發重繪(一個典型的例子是使用定時器為場景設置動畫),應該調用widget的update()函數來進行更新。
當調用paintGL(),resizeGL()或initializeGL()時,Widget的OpenGL渲染環境需要設為當前。如果需要從其他位置調用標准OpenGL API函數(例如,Widget的構造函數或自己的繪圖函數中),則必須首先調用makeCurrent()。
所有渲染都發生在OpenGL幀緩沖對象中,makeCurrent()確保它在渲染環境中,在paintGL()中的渲染代碼中創建和綁定其他幀緩沖對象時,不要使用ID 0重新綁定幀緩沖區,而是調用defaultFramebufferObject()來獲取應該綁定的ID。
QOpenGLWidget允許在平台支持時使用不同的OpenGL版本和配置文件。只需通過setFormat()設置請求的格式。但在同一窗口中有多個QOpenGLWidget,要求它們都使用相同的格式,或者至少不是環境共享的格式。要解決此問題,使用QSurfaceFormat :: setDefaultFormat(),而不是setFormat()。
注意:在請求OpenGL核心配置文件上下文時,在構造QApplication實例之前調用QSurfaceFormat :: setDefaultFormat()在某些平台(例如,macOS)上是必需的。這是為了確保上下文之間的資源共享保持功能,因為所有內部上下文都是使用正確的版本和配置文件創建的。
OpenGL Function Calls, Headers and QOpenGLFunctions
在進行OpenGL函數調用時,強烈建議避免直接調用函數。相反,更喜歡使用QOpenGLFunctions(在制作可移植應用程序時)或版本化變體(例如,QOpenGLFunctions_3_2_Core等,當針對現代的,僅限桌面的OpenGL時)。這樣,應用程序將在所有Qt構建配置中正常工作,包括執行動態OpenGL實現加載的應用程序,這意味着應用程序不直接鏈接到GL實現,因此直接函數調用是不可行的。
在paintGL()中,當前場景(context)始終可以通過調用QOpenGLContext :: currentContext()來訪問。從這個context中,可以通過調用QOpenGLContext :: functions()來檢索已經初始化的,准備好使用的QOpenGLFunctions實例。為每個GL調用添加前綴的替代方法是從QOpenGLFunctions繼承並在initializeGL()中調用QOpenGLFunctions :: initializeOpenGLFunctions()。
至於OpenGL標題,請注意,在大多數情況下,不需要直接包含任何標題,如GL.h.與OpenGL相關的Qt頭文件將包含qopengl.h,后者將包含適用於系統的標頭。這可能是OpenGL ES 3.x或2.0標頭,可用的最高版本,或系統提供的gl.h.此外,作為OpenGL和OpenGL ES的Qt的一部分,提供了擴展頭的副本(在某些系統上稱為glext.h)。這些將在可行的情況下自動包含在平台上。這意味着來自ARB,EXT,OES擴展的常量和函數指針typedef自動可用。
Code Examples
最簡單的例子
1 class MyGLWidget : public QOpenGLWidget 2 { 3 public: 4 MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { } 5
6 protected: 7 void initializeGL() 8 { 9 // Set up the rendering context, load shaders and other resources, etc.:
10 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); 11 f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 12 ... 13 } 14
15 void resizeGL(int w, int h) 16 { 17 // Update projection matrix and other size related settings:
18 m_projection.setToIdentity(); 19 m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f); 20 ... 21 } 22
23 void paintGL() 24 { 25 // Draw the scene:
26 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); 27 f->glClear(GL_COLOR_BUFFER_BIT); 28 ... 29 } 30
31 };
或通過使用QOpenGLFunction來代替OpenGL函數
1 class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions 2 { 3 ... 4 void initializeGL() 5 { 6 initializeOpenGLFunctions(); 7 glClearColor(...); 8 ... 9 } 10 ... 11 };
要獲得與給定OpenGL版本或配置文件兼容的context,或者要求深度和模板緩沖區,調用setFormat():
1 QOpenGLWidget *widget = new QOpenGLWidget(parent); 2 QSurfaceFormat format; 3 format.setDepthBufferSize(24); 4 format.setStencilBufferSize(8); 5 format.setVersion(3, 2); 6 format.setProfile(QSurfaceFormat::CoreProfile); 7 widget->setFormat(format); // must be called before the widget or its parent window gets shown
使用OpenGL 3.0+ context時,當可移植性不重要時,版本化的QOpenGLFunctions變體可以輕松訪問給定版本中可用的所有現代的OpenGL函數:
1 void paintGL() 2 { 3 QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>(); 4 ... 5 f->glDrawArraysInstanced(...); 6 ... 7 }
如上所述,全局設置所要求的格式以使其在應用程序的生命周期內應用於所有窗口和context更簡單且更魯棒。 以下是此示例:
1 int main(int argc, char **argv) 2 { 3 QApplication app(argc, argv); 4
5 QSurfaceFormat format; 6 format.setDepthBufferSize(24); 7 format.setStencilBufferSize(8); 8 format.setVersion(3, 2); 9 format.setProfile(QSurfaceFormat::CoreProfile); 10 QSurfaceFormat::setDefaultFormat(format); 11
12 MyWidget widget; 13 widget.show(); 14
15 return app.exec(); 16 }
Relation to QGLWidget
傳統的QtOpenGL模塊(以QGL為前綴的類)提供了一個名為QGLWidget的widget。QOpenGLWidget旨在成為它的替代品。因此,特別是在新的應用程序中,一般建議使用QOpenGLWidget。
雖然API非常相似,但兩者之間存在重要差異:QOpenGLWidget始終使用幀緩沖對象在屏幕外渲染。另一方面,QGLWidget使用原生窗口和曲面。后者在復雜的用戶界面中使用它時會引起問題,因為根據平台,這種本機子窗口小部件可能具有各種限制,例如關於堆疊命令。QOpenGLWidget通過不創建單獨的本機窗口來避免這種情況。
由於幀緩沖對象的支持,QOpenGLWidget的行為與QOpenGLWindow非常相似,更新行為設置為PartialUpdateBlit或PartialUpdateBlend。這意味着在paintGL()調用之間保留內容,以便可以進行增量渲染。使用QGLWidget(當然QOpenGLWindow具有默認的更新行為)通常不是這種情況,因為交換緩沖區會使后台緩沖區中的內容不確定。
注意:大多數應用程序不需要增量渲染,因為它們將在每次繪制調用時呈現視圖中的所有內容。在這種情況下,在paintGL()中盡早調用glClear()非常重要。這有助於使用基於圖塊的體系結構的移動GPU識別出圖塊緩沖區不需要使用幀緩沖區的先前內容重新加載。省略明確的呼叫可能導致此類系統的性能顯着下降。
注意:避免在QOpenGLWidget上調用winId()。此功能觸發創建本機窗口,從而降低性能並可能出現毛刺。
Differences to QGLWidget
除了framebuffer對象支持的主要概念差異之外,QOpenGLWidget和舊的QGLWidget之間存在許多較小的內部差異:
- 調用paintGL()時的OpenGL狀態。 QOpenGLWidget通過glViewport()設置視口。 它不執行任何清算。
- 當開始繪畫時通過QPainter清除。 與常規widget不同,QGLWidget默認為autoFillBackground的值為true。 然后,每次使用QPainter :: begin()時,它都會清除調色板的背景顏色。 QOpenGLWidget不遵循:autoFillBackground默認為false,就像任何其他widget一樣。 唯一的例外是當用作QGraphicsView等其他小部件的視口時。 在這種情況下,autoFillBackground將自動設置為true,以確保與基於QGLWidget的視口兼容。