Exemple de cube OpenGL ES 2.0
Montre comment faire pivoter manuellement un cube 3D texturé avec l'aide de l'utilisateur.
L'exemple Cube OpenGL ES 2.0 montre comment faire tourner manuellement un cube 3D texturé avec l'aide de l'utilisateur, en utilisant OpenGL ES 2.0 avec Qt 3D. Il montre comment gérer efficacement les géométries polygonales et comment écrire un simple vertex et fragment shader pour un pipeline graphique programmable. En outre, il montre comment utiliser les quaternions pour représenter l'orientation des objets 3D.
Cet exemple a été écrit pour OpenGL ES 2.0 mais il fonctionne également avec OpenGL de bureau car cet exemple est suffisamment simple et l'API OpenGL de bureau est en grande partie la même. Il se compile également sans le support d'OpenGL, mais il affiche alors une étiquette indiquant que le support d'OpenGL est nécessaire.

L'exemple se compose de deux classes :
MainWidgetétend QOpenGLWidget et contient l'initialisation et le dessin OpenGL ES 2.0 ainsi que la gestion de la souris et de la minuterieGeometryEnginegère les géométries polygonales. Transfère la géométrie des polygones vers les objets tampons de sommets et dessine les géométries à partir des objets tampons de sommets.
Nous commencerons par initialiser OpenGL ES 2.0 sur MainWidget.
Initialisation d'OpenGL ES 2.0
Puisque OpenGL ES 2.0 ne supporte plus le pipeline graphique fixe, il doit être implémenté par nous-mêmes. Cela rend le pipeline graphique très flexible mais en même temps il devient plus difficile parce que l'utilisateur doit implémenter le pipeline graphique pour faire fonctionner même l'exemple le plus simple. Cela rend également le pipeline graphique plus efficace car l'utilisateur peut décider quel type de pipeline est nécessaire pour l'application.
Tout d'abord, nous devons implémenter le vertex shader. Il reçoit les données des vertex et la matrice modèle-vue-projection (MVP) en tant que paramètres. Il transforme la position du sommet à l'aide de la matrice MVP en espace d'écran et transmet les coordonnées de la texture au nuanceur de fragment. Les coordonnées de texture seront automatiquement interpolées sur les faces du polygone.
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;
}Ensuite, nous devons implémenter la deuxième partie du pipeline graphique - le fragment shader. Pour cet exercice, nous devons implémenter un nuanceur de fragment qui gère la texturation. Il reçoit les coordonnées de la texture interpolée en tant que paramètre et recherche la couleur du fragment à partir de la texture donnée.
void main()
{
// Set fragment color from texture
gl_FragColor = texture2D(texture, v_texcoord);
}En utilisant QOpenGLShaderProgram, nous pouvons compiler, lier et lier le code du shader au pipeline graphique. Ce code utilise les fichiers de ressources Qt pour accéder au code source des shaders.
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(); }
Le code suivant permet la mise en mémoire tampon de la profondeur et l'élimination de la face arrière.
// Enable depth buffer glEnable(GL_DEPTH_TEST); // Enable back face culling glEnable(GL_CULL_FACE);
Chargement de textures à partir de fichiers de ressources Qt
L'interface QOpenGLWidget implémente des méthodes pour charger des textures depuis QImage vers la mémoire de texture OpenGL. Nous avons toujours besoin d'utiliser les fonctions fournies par OpenGL pour spécifier l'unité de texture OpenGL et configurer les options de filtrage des textures.
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); }
Géométrie cubique
Il y a plusieurs façons de rendre les polygones dans OpenGL, mais la plus efficace est d'utiliser uniquement les primitives de bandes triangulaires et de rendre les sommets à partir de la mémoire du matériel graphique. OpenGL dispose d'un mécanisme pour créer des objets tampons dans cette zone de mémoire et transférer les données des sommets dans ces tampons. Dans la terminologie d'OpenGL, ces objets sont appelés objets tampons de sommet (Vertex Buffer Objects - VBO).

C'est ainsi que les faces d'un cube se décomposent en triangles. Les sommets sont ordonnés de cette manière pour que l'ordre des sommets soit correct lors de l'utilisation des bandes de triangles. OpenGL détermine les faces avant et arrière des triangles en fonction de l'ordre des sommets. Par défaut, OpenGL utilise l'ordre inverse des aiguilles d'une montre pour les faces avant. Cette information est utilisée par l'élimination des faces arrières qui améliore les performances de rendu en ne rendant pas les faces arrières des triangles. De cette façon, le pipeline graphique peut omettre de rendre les côtés du triangle qui ne sont pas tournés vers l'écran.
La création d'objets tampons de vertex et le transfert de données vers ces objets sont assez simples à l'aide de QOpenGLBuffer. MainWidget s'assure que l'instance de GeometryEngine est créée et détruite avec le contexte OpenGL en cours. De cette manière, nous pouvons utiliser les ressources OpenGL dans le constructeur et effectuer un nettoyage adéquat dans le destructeur.
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));
Dessiner des primitives à partir de VBOs et indiquer au pipeline graphique programmable comment localiser les données de vertex nécessite quelques étapes. Tout d'abord, nous devons lier les VBO à utiliser. Ensuite, nous lions les noms des attributs du programme de shader et configurons le type de données qu'il possède dans le VBO lié. Enfin, nous dessinerons des primitives de bandes de triangles en utilisant les indices de l'autre 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); }
Projection en perspective
En utilisant les méthodes d'aide de QMatrix4x4, il est très facile de calculer la matrice de projection perspective. Cette matrice est utilisée pour projeter les sommets dans l'espace de l'écran.
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); }
Orientation de l'objet 3D
Les quaternions sont un moyen pratique de représenter l'orientation d'un objet 3D. Les quaternions impliquent des mathématiques assez complexes, mais heureusement, toutes les mathématiques nécessaires aux quaternions sont implémentées dans QQuaternion. Cela nous permet de stocker l'orientation d'un cube en quaternions et de faire tourner un cube autour d'un axe donné, ce qui est assez simple.
Le code suivant calcule l'axe de rotation et la vitesse angulaire en fonction des événements de la souris.
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 est utilisé pour animer la scène et mettre à jour l'orientation du cube. Les rotations peuvent être concaténées simplement en multipliant les quaternions.
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 matrice modèle-vue est calculée en utilisant les quaternions et en déplaçant le monde sur l'axe Z. Cette matrice est multipliée par la matrice de projection. Cette matrice est multipliée avec la matrice de projection pour obtenir la matrice MVP pour le programme 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.