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
最簡單的例子
class MyGLWidget : public QOpenGLWidget
{
public:
MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }
protected:
void initializeGL()
{
// 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)
{
// 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()
{
// Draw the scene:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClear(GL_COLOR_BUFFER_BIT);
...
}
};
或通過使用QOpenGLFunction來代替OpenGL函數
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
...
void initializeGL()
{
initializeOpenGLFunctions();
glClearColor(...);
...
}
...
};
要獲得與給定OpenGL版本或配置文件兼容的context,或者要求深度和模板緩衝區,調用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 3.0+ context時,當可移植性不重要時,版本化的QOpenGLFunctions變體可以輕鬆訪問給定版本中可用的所有現代的OpenGL函數:
void paintGL()
{
QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
...
f->glDrawArraysInstanced(...);
...
}
如上所述,全局設置所要求的格式以使其在應用程序的生命週期內應用於所有窗口和context更簡單且更魯棒。 以下是此示例:
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();
}
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的視口兼容。