Bonjour à tous !
Je cherche a implémenter du color picking pour changer la couleur d'objets au survol de la souris, dans un projet en core OpenGL et Qt5.
J'ai cherché a éviter les surcouches à OpenGL de Qt qui complexifiaient mon apprentissage, et jusqu'à présent tout marchait comme sur des roulettes, aussi bien l'affichage, les shaders, les passages de variable uniformes, etc. À part mon objet principal qui hérite de QOpenGLWidget, et quelques surcouches personnelles qui ont besoin d'hériter de QOpenGLFunctions_3_3_Core pour accéder aux types et fonctionnalités d'OpenGL, tout est de l'OpenGL bien brut.
Pour l'implémentation, j'ai lu et suivi une partie du tutoriel sur OGLdev. Ainsi, je dispose de l'exact même objet PickingTexture. Mon fragment shader de picking se contentent pour le moment de renvoyer toujours le même vecteur de trois floats (1.0, 0.0, 0.0).
Première surprise, dans ma fonction paintGL() l'ajout de la phase de picking avant mon habituelle phase d'affichage a supprimé l'affichage des primitives à l'écran. Aucune erreur OpenGL.
Je supprime des lignes pour en faire un code minimal, je me rends compte que cela provient de l'appel à glBendFrameBuffer.
Je ne parviens pas à repasser sur le framebuffer par défaut (nommé back buffer, il me semble). Je lis pourtant bien dans OpenGL SuperBible, seventh edition que passer 0 au second paramètre de cette fonction doit permettre de faire le rendu dans le buffer par défaut, celui géré par le système de fenêtres. Est-ce Qt qui me jouerait un tour ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 void Display::paintGL(void) { // Picking phase (super raccourcie) /* --/!\-- Commenter cette ligne permet d'afficher de nouveau les vertices --/!\-- */ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // Display phase (qui marche si on décommente la ligne au dessus) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _displayShader->use(); glBindVertexArray(this->_VAO); _camera.attach(*_displayShader, _screenWidth, _screenHeight); glDrawArrays(GL_TRIANGLES, 0, (_peptide->getVerticesArraySize()) / 6); }
Un peu en râlant de cette solution de fortune, et trouvant peu d'aide sur le net, je me contente de faire la phase de picking après la phase de rendu. Seulement maintenant c'est glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &Pixel) qui me joue un tour. Au premier clic sur le rendu, la fonction readPixel() me fournit un PixelInfo avec trois variables settées n'importe comment. Les clics suivants, je n'ai que des zéro, valeurs par défaut des attributs de PixelInfo.
Ci-dessous le code de readPixel. Il s'agit du même code que celui d'OGL.
Voici ma fonction PaintGL() :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 PickingTexture::PixelInfo PickingTexture::readPixel(unsigned int x, unsigned int y) { glBindFramebuffer(GL_READ_FRAMEBUFFER, _fbo); glReadBuffer(GL_COLOR_ATTACHMENT0); PixelInfo Pixel; glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &Pixel); /* Lui là, il n'édite plus rien dès le second clic */ glReadBuffer(GL_NONE); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); return Pixel; }
Mon vertex shader de presque-picking (c'est surtout un test du remplissage du FBO actuellement) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 void Display::paintGL(void) { /* Display phase */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _displayShader->use(); glBindVertexArray(this->_VAO); _camera.attach(*_displayShader, _screenWidth, _screenHeight); glDrawArrays(GL_TRIANGLES, 0, (_peptide->getVerticesArraySize()) / 6); /* Picking phase */ _pickingShader->use(); _camera.attach(*_pickingShader, _screenWidth, _screenHeight); _pickMap.enableWriting(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, (_peptide->getVerticesArraySize()) / 6); _pickMap.disableWriting(); glBindVertexArray(0); }
Mon fragment shader
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 #version 330 core layout (location = 0) in vec3 position; uniform mat4 model; uniform mat4 view; uniform mat4 projection; out vec3 pos; void main() { gl_Position = projection * view * model * vec4(position, 1.0); pos = vec3(gl_Position.x, gl_Position.y, gl_Position.z); }
Ma PickMap est une PickingTexture, qui est exactement la même classe que celle du tutorial.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 #version 330 core out vec3 FragColor; in vec3 pos; void main() { FragColor = pos; }
Les seules solutions a ce problème que j'ai trouvé sur le net consistaient à dire "Instancie un QOpenGLFramebufferObject que tu exportes dans une QImage, puis tu lis le pixel x y depuis la QImage". Mais cette solution me déplaît profondément (déjà car des sucouches Qt, et ensuite car cela fait des créations d'instances en plus à chaque frame pour quelque chose de relativement simple a faire en temps normal).
J'ai l'impression que les deux problèmes rencontrés sont fondamentalement liés par une même cause. Qu'en pensez-vous ? Est-ce mon code qui comporte des erreurs, ou bien une incompatibilité avec une utilisation dans un contexte Qt ? Ai-je mal compris un principe d'utilisation des FBO ?
Je remercie d'avance tous ceux qui s’intéressent à mon problème![]()
Partager