Cubo OpenGL ES 2.0 ejemplo
Muestra cómo rotar manualmente un cubo 3D texturizado con la entrada del usuario.
El ejemplo Cube OpenGL ES 2.0 muestra cómo rotar manualmente un cubo 3D texturizado con la entrada del usuario, utilizando OpenGL ES 2.0 con Qt. Muestra cómo manejar geometrías poligonales de forma eficiente y cómo escribir un sombreador de vértices y fragmentos simple para un canal gráfico programable. Además, muestra cómo utilizar cuaterniones para representar la orientación de objetos 3D.
Este ejemplo ha sido escrito para OpenGL ES 2.0 pero funciona también en OpenGL de escritorio porque este ejemplo es lo suficientemente simple y para la mayoría de las partes la API OpenGL de escritorio es la misma. También compila sin soporte OpenGL pero entonces sólo muestra una etiqueta indicando que se requiere soporte OpenGL.

El ejemplo consiste en dos clases:
MainWidgetextiende QOpenGLWidget y contiene inicialización OpenGL ES 2.0 y manejo de eventos de dibujo, ratón y temporizador.GeometryEnginemaneja geometrías poligonales. Transfiere geometría poligonal a objetos de buffer de vértices y dibuja geometrías desde objetos de buffer de vértices.
Comenzaremos inicializando OpenGL ES 2.0 en MainWidget.
Inicializando OpenGL ES 2.0
Dado que OpenGL ES 2.0 ya no soporta un pipeline gráfico fijo, tenemos que implementarlo nosotros mismos. Esto hace que el canal de gráficos sea muy flexible, pero al mismo tiempo se hace más difícil porque el usuario tiene que implementar el canal de gráficos para hacer funcionar incluso el ejemplo más simple. También hace que el canal gráfico sea más eficiente porque el usuario puede decidir qué tipo de canal es necesario para la aplicación.
Primero tenemos que implementar el sombreador de vértices. Obtiene los datos de vértices y la matriz modelo-vista-proyección (MVP) como parámetros. Transforma la posición de los vértices usando la matriz MVP al espacio de la pantalla y pasa las coordenadas de la textura al fragment shader. Las coordenadas de textura se interpolan automáticamente en las caras de los polígonos.
void main()
{
// Calculate vertex position in screen space
gl_Position = mvp_matrix * a_position;
// Pass texture coordinate to fragment shader
// Value will be automatically interpolated to fragments inside polygon faces
v_texcoord = a_texcoord;
}Después de eso tenemos que implementar la segunda parte de la tubería de gráficos - fragment shader. Para este ejercicio necesitamos implementar el fragment shader que maneja la textura. Obtiene la coordenada de la textura interpolada como parámetro y busca el color del fragmento a partir de la textura dada.
void main()
{
// Set fragment color from texture
gl_FragColor = texture2D(texture, v_texcoord);
}Usando QOpenGLShaderProgram podemos compilar, enlazar y vincular el código del sombreador al canal gráfico. Este código utiliza archivos de recursos Qt para acceder al código fuente del shader.
void MainWidget::initShaders() { // Compile vertex shader if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl")) close(); // Compile fragment shader if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl")) close(); // Link shader pipeline if (!program.link()) close(); // Bind shader pipeline for use if (!program.bind()) close(); }
El siguiente código habilita el buffering de profundidad y el back face culling.
// Enable depth buffer glEnable(GL_DEPTH_TEST); // Enable back face culling glEnable(GL_CULL_FACE);
Carga de texturas desde archivos de recursos Qt
La interfaz QOpenGLWidget implementa métodos para cargar texturas desde QImage a la memoria de texturas OpenGL. Aún necesitamos utilizar las funciones proporcionadas por OpenGL para especificar la unidad de textura OpenGL y configurar las opciones de filtrado de texturas.
void MainWidget::initTextures() { // Load cube.png image texture = new QOpenGLTexture(QImage(":/cube.png").flipped()); // Set nearest filtering mode for texture minification texture->setMinificationFilter(QOpenGLTexture::Nearest); // Set bilinear filtering mode for texture magnification texture->setMagnificationFilter(QOpenGLTexture::Linear); // Wrap texture coordinates by repeating // f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2) texture->setWrapMode(QOpenGLTexture::Repeat); }
Geometría cúbica
Hay muchas formas de renderizar polígonos en OpenGL, pero la más eficiente es utilizar sólo primitivas de banda triangular y renderizar los vértices desde la memoria del hardware de gráficos. OpenGL tiene un mecanismo para crear objetos búfer a esta área de memoria y transferir datos de vértices a estos búferes. En la terminología de OpenGL se denominan objetos de búfer de vértices (VBO).

Así es como las caras de los cubos se descomponen en triángulos. Los vértices se ordenan de esta manera para que el orden de los vértices sea el correcto utilizando tiras de triángulos. OpenGL determina la cara frontal y posterior del triángulo basándose en el orden de los vértices. Por defecto OpenGL utiliza el orden contrario a las agujas del reloj para las caras frontales. Esta información es utilizada por el "back face culling" que mejora el rendimiento del renderizado al no renderizar las caras traseras de los triángulos. De esta forma, la línea gráfica puede omitir el renderizado de las caras del triángulo que no están orientadas hacia la pantalla.
La creación de objetos de búfer de vértices y la transferencia de datos a ellos es bastante simple usando QOpenGLBuffer. MainWidget se asegura de que la instancia GeometryEngine es creada y destruida con el contexto OpenGL actual. De esta forma podemos utilizar recursos OpenGL en el constructor y realizar una limpieza adecuada en el destructor.
GeometryEngine::GeometryEngine() : indexBuf(QOpenGLBuffer::IndexBuffer) { initializeOpenGLFunctions(); // Generate 2 VBOs arrayBuf.create(); indexBuf.create(); // Initializes cube geometry and transfers it to VBOs initCubeGeometry(); } GeometryEngine::~GeometryEngine() { arrayBuf.destroy(); indexBuf.destroy(); } // Transfer vertex data to VBO 0 arrayBuf.bind(); arrayBuf.allocate(vertices, 24 * sizeof(VertexData)); // Transfer index data to VBO 1 indexBuf.bind(); indexBuf.allocate(indices, 34 * sizeof(GLushort));
Dibujar primitivas a partir de VBOs y decirle al pipeline gráfico programable cómo localizar los datos de vértices requiere unos pocos pasos. Primero necesitamos enlazar los VBOs a utilizar. Después de eso vinculamos los nombres de los atributos del programa shader y configuramos qué tipo de datos tiene en el VBO vinculado. Finalmente dibujaremos primitivas triangulares usando los índices del otro VBO.
void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program) { // Tell OpenGL which VBOs to use arrayBuf.bind(); indexBuf.bind(); // Offset for position quintptr offset = 0; // Tell OpenGL programmable pipeline how to locate vertex position data int vertexLocation = program->attributeLocation("a_position"); program->enableAttributeArray(vertexLocation); program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData)); // Offset for texture coordinate offset += sizeof(QVector3D); // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data int texcoordLocation = program->attributeLocation("a_texcoord"); program->enableAttributeArray(texcoordLocation); program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData)); // Draw cube geometry using indices from VBO 1 glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, nullptr); }
Proyección en Perspectiva
Usando los métodos de ayuda de QMatrix4x4 es realmente fácil calcular la matriz de proyección perpectiva. Esta matriz se utiliza para proyectar los vértices al espacio de la pantalla.
void MainWidget::resizeGL(int w, int h) { // Calculate aspect ratio qreal aspect = qreal(w) / qreal(h ? h : 1); // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees const qreal zNear = 3.0, zFar = 7.0, fov = 45.0; // Reset projection projection.setToIdentity(); // Set perspective projection projection.perspective(fov, aspect, zNear, zFar); }
Orientación del Objeto 3D
Los cuaterniones son una forma práctica de representar la orientación de un objeto 3D. Los cuaterniones implican matemáticas bastante complejas pero afortunadamente todas las matemáticas necesarias detrás de los cuaterniones están implementadas en QQuaternion. Eso nos permite almacenar la orientación del cubo en cuaterniones y rotar el cubo alrededor de un eje dado es bastante simple.
El siguiente código calcula el eje de rotación y la velocidad angular basándose en los eventos del ratón.
void MainWidget::mousePressEvent(QMouseEvent *e) { // Save mouse press position mousePressPosition = QVector2D(e->position()); } void MainWidget::mouseReleaseEvent(QMouseEvent *e) { // Mouse release position - mouse press position QVector2D diff = QVector2D(e->position()) - mousePressPosition; // Rotation axis is perpendicular to the mouse position difference // vector QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized(); // Accelerate angular speed relative to the length of the mouse sweep qreal acc = diff.length() / 100.0; // Calculate new rotation axis as weighted sum rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized(); // Increase angular speed angularSpeed += acc; }
QBasicTimer se utiliza para animar la escena y actualizar la orientación del cubo. Las rotaciones se pueden concatenar simplemente multiplicando cuaterniones.
void MainWidget::timerEvent(QTimerEvent *) { // Decrease angular speed (friction) angularSpeed *= 0.99; // Stop rotation when speed goes below threshold if (angularSpeed < 0.01) { angularSpeed = 0.0; } else { // Update rotation rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation; // Request an update update(); } }
La matriz modelo-vista se calcula usando el quaternion y moviendo el mundo por el eje Z. Esta matriz se multiplica con la matriz de proyección para obtener la matriz MVP para el programa shader.
// Calculate model view transformation QMatrix4x4 matrix; matrix.translate(0.0, 0.0, -5.0); matrix.rotate(rotation); // Set modelview-projection matrix program.setUniformValue("mvp_matrix", projection * matrix);
© 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.