Beispiel für ein OpenGL-Fenster
Dieses Beispiel zeigt, wie man eine minimale Anwendung auf Basis von „ QWindow “ erstellt, um OpenGL zu nutzen.

Hinweis: Dies ist ein Low-Level-Beispiel für die Verwendung von „ QWindow “ mit OpenGL. In der Praxis sollten Sie die Verwendung der übergeordneten Klasse „ QOpenGLWindow “ in Betracht ziehen. Eine Demonstration der Komfortklasse „ QOpenGLWindow “ finden Sie im Beispiel „Hello GLES3 “.
Superklasse „OpenGLWindow“
Unsere Klasse „OpenGLWindow“ fungiert als API, von der dann Unterklassen abgeleitet werden, um das eigentliche Rendering durchzuführen. Sie verfügt über Funktionen, um den Aufruf von `render()` anzufordern – entweder sofort mit `renderNow()` oder, sobald die Ereignisschleife die Verarbeitung des aktuellen Ereignisbatches abgeschlossen hat, mit `renderLater()`. Die OpenGLWindow-Unterklasse kann entweder `render()` für OpenGL-basiertes Rendering neu implementieren oder `render(QPainter *)` für das Rendering mit einem „ QPainter “. Verwenden Sie `OpenGLWindow::setAnimating(true)`, damit `render()` mit der vertikalen Bildwiederholrate aufgerufen wird, vorausgesetzt, die vertikale Synchronisation ist in den zugrunde liegenden OpenGL-Treibern aktiviert.
In der Klasse, die das OpenGL-Rendering durchführt, sollten Sie in der Regel von ` QOpenGLFunctions` erben, wie es unser `OpenGLWindow` tut, um plattformunabhängigen Zugriff auf OpenGL ES 2.0-Funktionen zu erhalten. Durch die Vererbung von ` QOpenGLFunctions ` erhalten die darin enthaltenen OpenGL-Funktionen Vorrang, und Sie müssen sich keine Gedanken über die Auflösung dieser Funktionen machen, wenn Ihre Anwendung sowohl mit OpenGL als auch mit OpenGL ES 2.0 funktionieren soll.
class OpenGLWindow : public QWindow, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit OpenGLWindow(QWindow *parent = nullptr);
~OpenGLWindow();
virtual void render(QPainter *painter);
virtual void render();
virtual void initialize();
void setAnimating(bool animating);
public slots:
void renderLater();
void renderNow();
protected:
bool event(QEvent *event) override;
void exposeEvent(QExposeEvent *event) override;
private:
bool m_animating = false;
QOpenGLContext *m_context = nullptr;
QOpenGLPaintDevice *m_device = nullptr;
};Der Oberflächentyp des Fensters muss auf „ QSurface::OpenGLSurface “ gesetzt werden, um anzugeben, dass das Fenster für das OpenGL-Rendering verwendet werden soll und nicht für das Rendern von Rasterinhalten mit „ QPainter “ unter Verwendung eines „ QBackingStore “.
OpenGLWindow::OpenGLWindow(QWindow *parent)
: QWindow(parent)
{
setSurfaceType(QWindow::OpenGLSurface);
}Jede erforderliche OpenGL-Initialisierung kann durch Überschreiben der Funktion `initialize()` erfolgen, die einmal vor dem ersten Aufruf von `render()` mit einem gültigen aktuellen ` QOpenGLContext` aufgerufen wird. Wie im folgenden Codeausschnitt zu sehen ist, sind die Standardimplementierungen von `render(QPainter *)` und `initialize()` leer, während die Standardimplementierung von `render()` ein ` QOpenGLPaintDevice ` initialisiert und anschließend `render(QPainter *)` aufruft.
void OpenGLWindow::render(QPainter *painter)
{
Q_UNUSED(painter);
}
void OpenGLWindow::initialize()
{
}
void OpenGLWindow::render()
{
if (!m_device)
m_device = new QOpenGLPaintDevice;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
m_device->setSize(size() * devicePixelRatio());
m_device->setDevicePixelRatio(devicePixelRatio());
QPainter painter(m_device);
render(&painter);
}Die Funktion `renderLater()` ruft einfach ` QWindow::requestUpdate()` auf, um eine Aktualisierung für den Zeitpunkt zu planen, zu dem das System bereit ist, die Darstellung zu aktualisieren.
Wir rufen außerdem `renderNow()` auf, wenn wir ein `expose`-Ereignis erhalten. Das `exposeEvent()` ist die Benachrichtigung an das Fenster, dass sich dessen Sichtbarkeit auf dem Bildschirm geändert hat. Wenn das `expose`-Ereignis empfangen wird, können Sie mit ` QWindow::isExposed()` abfragen, ob das Fenster derzeit sichtbar ist oder nicht. Führen Sie kein Rendering durch und rufen Sie „ QOpenGLContext::swapBuffers()“ nicht für ein Fenster auf, bevor es sein erstes „expose“-Ereignis empfangen hat, da zuvor seine endgültige Größe möglicherweise noch unbekannt ist und darüber hinaus das Gerenderte möglicherweise gar nicht auf dem Bildschirm angezeigt wird.
void OpenGLWindow::renderLater()
{
requestUpdate();
}
bool OpenGLWindow::event(QEvent *event)
{
switch (event->type()) {
case QEvent::UpdateRequest:
renderNow();
return true;
default:
return QWindow::event(event);
}
}
void OpenGLWindow::exposeEvent(QExposeEvent *event)
{
Q_UNUSED(event);
if (isExposed())
renderNow();
}In `renderNow()` brechen wir die Ausführung ab, wenn wir derzeit nicht sichtbar sind; in diesem Fall wird das Rendern verzögert, bis wir tatsächlich ein „Expose“-Ereignis erhalten. Falls noch nicht geschehen, erstellen wir das ` QOpenGLContext ` mit demselben ` QSurfaceFormat `, wie es für das `OpenGLWindow` festgelegt wurde, und rufen `initialize()` für die Unterklasse sowie `initializeOpenGLFunctions()` auf, damit die Oberklasse ` QOpenGLFunctions ` dem richtigen ` QOpenGLContext` zugeordnet wird. In jedem Fall machen wir den Kontext aktuell, indem wir ` QOpenGLContext::makeCurrent()` aufrufen, rufen `render()` auf, um das eigentliche Rendern durchzuführen, und planen schließlich, dass die gerenderten Inhalte sichtbar gemacht werden, indem wir ` QOpenGLContext::swapBuffers()` mit dem `OpenGLWindow` als Parameter aufrufen.
Sobald das Rendern eines Frames unter Verwendung eines OpenGL-Kontexts durch den Aufruf von ` QOpenGLContext::makeCurrent()` initiiert wurde – wobei die zu rendernde Oberfläche als Parameter übergeben wird –, können OpenGL-Befehle ausgegeben werden. Die Befehle können entweder direkt durch Einbinden von <qopengl.h> erteilt werden, wodurch auch die OpenGL-Header des Systems eingebunden werden, oder durch Verwendung von ` QOpenGLFunctions`, das der Einfachheit halber vererbt werden kann oder über ` QOpenGLContext::functions()` aufgerufen wird. ` QOpenGLFunctions ` bietet Zugriff auf alle OpenGL-Aufrufe auf OpenGL ES 2.0-Ebene, die weder in OpenGL ES 2.0 noch in Desktop-OpenGL standardmäßig enthalten sind. Weitere Informationen zu den OpenGL- und OpenGL ES-APIs finden Sie in der offiziellen OpenGL-Registry und der Khronos OpenGL ES API Registry.
Wenn die Animation mit `OpenGLWindow::setAnimating(true)` aktiviert wurde, rufen wir `renderLater()` auf, um eine weitere Aktualisierungsanforderung zu planen.
void OpenGLWindow::renderNow()
{
if (!isExposed())
return;
bool needsInitialize = false;
if (!m_context) {
m_context = new QOpenGLContext(this);
m_context->setFormat(requestedFormat());
m_context->create();
needsInitialize = true;
}
m_context->makeCurrent(this);
if (needsInitialize) {
initializeOpenGLFunctions();
initialize();
}
render();
m_context->swapBuffers(this);
if (m_animating)
renderLater();
}Das Aktivieren der Animation plant ebenfalls eine Aktualisierungsanforderung ein, wie im folgenden Codeausschnitt gezeigt.
void OpenGLWindow::setAnimating(bool animating)
{
m_animating = animating;
if (animating)
renderLater();
}Beispiel für eine OpenGL-Rendering-Unterklasse
Hier erstellen wir eine Unterklasse von `OpenGLWindow`, um zu zeigen, wie man mit OpenGL ein rotierendes Dreieck rendert. Durch die indirekte Unterklassifizierung von ` QOpenGLFunctions ` erhalten wir Zugriff auf alle Funktionen auf OpenGL ES 2.0-Ebene.
class TriangleWindow : public OpenGLWindow
{
public:
using OpenGLWindow::OpenGLWindow;
void initialize() override;
void render() override;
private:
GLint m_matrixUniform = 0;
QOpenGLBuffer m_vbo;
QOpenGLShaderProgram *m_program = nullptr;
int m_frame = 0;
};In unserer Hauptfunktion initialisieren wir ` QGuiApplication ` und instanziieren unser `TriangleOpenGLWindow`. Wir übergeben ihm ein ` QSurfaceFormat `, in dem wir vier Samples für Multisample-Antialiasing sowie eine Standardgeometrie angeben. Da wir eine Animation wünschen, rufen wir die oben erwähnte Funktion `setAnimating()` mit dem Argument `true` auf.
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QSurfaceFormat format;
format.setSamples(16);
TriangleWindow window;
window.setFormat(format);
window.resize(640, 480);
window.show();
window.setAnimating(true);
return app.exec();
}Der folgende Codeausschnitt zeigt das in diesem Beispiel verwendete OpenGL-Shader-Programm. Die Vertex- und Fragment-Shader sind relativ einfach und führen eine Vertex-Transformation sowie eine interpolierte Vertex-Einfärbung durch.
static const char *vertexShaderSource = "attribute highp vec4 posAttr;\n"
"attribute lowp vec4 colAttr;\n"
"varying lowp vec4 col;\n"
"uniform highp mat4 matrix;\n"
"void main() {\n"
" col = colAttr;\n"
" gl_Position = matrix * posAttr;\n"
"}\n";
static const char *fragmentShaderSource = "varying lowp vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n";Hier ist der Code, der die Shader lädt und das Shader-Programm initialisiert. Durch die Verwendung von „ QOpenGLShaderProgram “ anstelle von nacktem OpenGL profitieren wir von dem Vorteil, dass die Qualifizierer „highp“, „mediump“ und „lowp“ bei Desktop-OpenGL entfernt werden, da sie dort nicht Teil des Standards sind. Wir speichern die Speicherorte der Attribute und Uniforms in Member-Variablen, um zu vermeiden, dass wir in jedem Frame die Speicherorte nachschlagen müssen.
void TriangleWindow::initialize()
{
static const GLfloat vertices_colors[] = { +0.0f, +0.707f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.500f, 0.0f, 1.0f, 0.0f,
+0.5f, -0.500f, 0.0f, 0.0f, 1.0f };
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(vertices_colors, sizeof(vertices_colors));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), nullptr);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
reinterpret_cast<void *>(2 * sizeof(GLfloat)));
m_program = new QOpenGLShaderProgram(this);
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
m_program->bindAttributeLocation("posAttr", 0);
m_program->bindAttributeLocation("colAttr", 1);
m_program->link();
m_program->bind();
m_matrixUniform = m_program->uniformLocation("matrix");
Q_ASSERT(m_matrixUniform != -1);
}Abschließend folgt hier unsere Funktion `render()`, in der wir mit OpenGL den Viewport einrichten, den Hintergrund löschen und ein rotierendes Dreieck rendern.
void TriangleWindow::render()
{
const qreal retinaScale = devicePixelRatio();
glViewport(0, 0, width() * retinaScale, height() * retinaScale);
glClear(GL_COLOR_BUFFER_BIT);
m_program->bind();
QMatrix4x4 matrix;
matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
matrix.translate(0, 0, -2);
matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0);
m_program->setUniformValue(m_matrixUniform, matrix);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
m_program->release();
++m_frame;
}© 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.