Ejemplo de ventana OpenGL
Este ejemplo muestra cómo crear una aplicación mínima basada en QWindow con el propósito de utilizar OpenGL.

Nota: Este es un ejemplo de bajo nivel de cómo usar QWindow con OpenGL. En la práctica deberías considerar usar la clase de más alto nivel QOpenGLWindow. Vea el Ejemplo Hello GLES3 para una demostración de la clase de conveniencia QOpenGLWindow.
Super Clase OpenGLWindow
Nuestra clase OpenGLWindow actúa como una API que luego es subclasificada para hacer el renderizado real. Tiene funciones para hacer una solicitud para que render() sea llamada, ya sea inmediatamente con renderNow() o tan pronto como el bucle de eventos haya terminado de procesar el lote actual de eventos con renderLater(). La subclase OpenGLWindow puede reimplementar render() para renderizado basado en OpenGL, o render(QPainter *) para renderizado con QPainter. Utilice OpenGLWindow::setAnimating(true) para que render() sea llamada a la tasa de refresco vertical, asumiendo que la sincronización vertical está habilitada en los drivers OpenGL subyacentes.
En la clase que hace el renderizado OpenGL, normalmente querrás heredar de QOpenGLFunctions, como hace nuestra OpenGLWindow, para obtener acceso independiente de la plataforma a las funciones OpenGL ES 2.0. Al heredar de QOpenGLFunctions las funciones OpenGL que contiene tendrán precedencia, y no tendrás que preocuparte de resolver esas funciones si quieres que tu aplicación funcione tanto con OpenGL como con OpenGL ES 2.0.
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; };
El tipo de superficie de la ventana debe establecerse en QSurface::OpenGLSurface para indicar que la ventana se va a utilizar para el renderizado OpenGL y no para el renderizado de contenido rasterizado con QPainter utilizando un QBackingStore.
OpenGLWindow::OpenGLWindow(QWindow *parent) : QWindow(parent) { setSurfaceType(QWindow::OpenGLSurface); }
Cualquier inicialización OpenGL necesaria se puede hacer sobrescribiendo la función initialize(), que se llama una vez antes de la primera llamada a render(), con una corriente válida QOpenGLContext. Como puede verse en el siguiente fragmento de código, las implementaciones por defecto de render(QPainter *) e initialize() están vacías, mientras que la implementación por defecto de render() inicializa un QOpenGLPaintDevice y luego llama a render(QPainter *).
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); }
La función renderLater() simplemente llama a QWindow::requestUpdate() para programar una actualización para cuando el sistema esté listo para repintar.
También llamamos a renderNow() cuando recibimos un evento expose. El exposeEvent() es la notificación a la ventana de que su exposición, es decir visibilidad, en la pantalla ha cambiado. Cuando se recibe el evento expose se puede consultar QWindow::isExposed() para saber si la ventana está actualmente expuesta o no. No renderices o llames a QOpenGLContext::swapBuffers() en una ventana antes de que haya recibido su primer evento expose, ya que antes de eso su tamaño final podría ser desconocido, y además lo que se renderiza podría no terminar en la pantalla.
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(); }
En renderNow() devolvemos si no estamos actualmente expuestos, en cuyo caso el renderizado se retrasa hasta que realmente recibamos un evento de exposición. Si aún no lo hemos hecho, creamos el QOpenGLContext con el mismo QSurfaceFormat que se estableció en el OpenGLWindow, y llamamos a initialize() por el bien de la subclase, y a initializeOpenGLFunctions() para que la superclase QOpenGLFunctions se asocie con el QOpenGLContext correcto. En cualquier caso hacemos el contexto actual llamando a QOpenGLContext::makeCurrent(), llamamos a render() para hacer el renderizado real, y finalmente programamos para que el contenido renderizado se haga visible llamando a QOpenGLContext::swapBuffers() con la OpenGLWindow como parámetro.
Una vez que el renderizado de un fotograma utilizando un contexto OpenGL se inicia llamando a QOpenGLContext::makeCurrent(), dando como parámetro la superficie sobre la que se va a renderizar, se pueden emitir comandos OpenGL. Los comandos pueden ser emitidos bien directamente incluyendo <qopengl.h>, que también incluye las cabeceras OpenGL del sistema, o bien utilizando QOpenGLFunctions, del que se puede heredar por conveniencia, o al que se puede acceder utilizando QOpenGLContext::functions(). QOpenGLFunctions da acceso a todas las llamadas OpenGL de nivel OpenGL ES 2.0 que no son ya estándar tanto en OpenGL ES 2.0 como en OpenGL de escritorio. Para obtener más información sobre las API OpenGL y OpenGL ES, consulta el registro oficial de OpenGL y el registro de API OpenGL ES de Khronos.
Si se ha habilitado la animación con OpenGLWindow::setAnimating(true), llamamos a renderLater() para programar otra petición de actualización.
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(); }
Activar la animación también programa una petición de actualización como se muestra en el siguiente fragmento de código.
void OpenGLWindow::setAnimating(bool animating) { m_animating = animating; if (animating) renderLater(); }
Ejemplo de subclase de renderizado OpenGL
Aquí subclasificamos OpenGLWindow para mostrar cómo hacer OpenGL para renderizar un triángulo giratorio. Subclasificando indirectamente QOpenGLFunctions obtenemos acceso a toda la funcionalidad OpenGL ES 2.0.
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; };
En nuestra función principal inicializamos QGuiApplication e instanciamos nuestro TriangleOpenGLWindow. Le damos un QSurfaceFormat especificando que queremos cuatro muestras de antialiasing multimuestra, así como una geometría por defecto. Como queremos tener animación llamamos a la función setAnimating() antes mencionada con un argumento de true.
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(); }
El siguiente fragmento de código muestra el programa de sombreado OpenGL utilizado en este ejemplo. Los shaders de vértices y fragmentos son relativamente simples, haciendo transformación de vértices y coloreado interpolado de vértices.
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";
Este es el código que carga los sombreadores e inicializa el programa de sombreado Al utilizar QOpenGLShaderProgram en lugar de OpenGL sin procesar, obtenemos la comodidad de eliminar los calificadores highp, mediump y lowp en OpenGL de escritorio, donde no forman parte del estándar. Almacenamos las localizaciones de atributos y uniformes en variables miembro para evitar tener que buscar la localización en cada fotograma.
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); }
Finalmente, aquí está nuestra función render(), donde usamos OpenGL para configurar la ventana gráfica, limpiar el fondo, y renderizar un triángulo giratorio.
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.