IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenGL Discussion :

Implémentation d'un color picking sous Qt5 [OpenGL 4.x]


Sujet :

OpenGL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut Implémentation d'un color picking sous Qt5
    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.
    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);
    }
    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 ?

    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.
    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;
    }
    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
    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 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
     
    #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);
    }
    Mon fragment shader
    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;
    }
    Ma PickMap est une PickingTexture, qui est exactement la même classe que celle du tutorial.

    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

  2. #2
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Par défaut
    Hello,

    QOpenGLWidget fait son rendu dans son propre FramebufferObject (ce qui n'était pas le cas avec QGLWidget). As-tu essayé de binder sur celui-ci ?

    => defaultFramebufferObject

  3. #3
    Invité
    Invité(e)
    Par défaut
    Salut Jbx 2.0b

    Effectivement, binder ce FBO a redonné un comportement logique d'affichage, et me permet de remettre la phase de picking avant la phase d'affichage. Les vertices s'affichent. Merci !

    Par contre le picking ne fonctionne toujours pas. Je viens de faire en sorte que mon fragment shader de picking retourne de nouveau (1.0, 0.0, 0.0) quel que soit le cas, et j'observe le même comportement : des valeurs hasardeuses au premier clic (le dernier exemple en date 0.0980392 0.109804 0.129412) puis trois zéros pour chacun des clics suivants.
    J'ai réalisé que j'avais le même comportement lorsque supprimais le code de picking de ma fonction paintGL(). Ce qui veut dire que c'est un résultat possible quand rien n'a été écrit dans le FBO, qu'il a seulement été initialisé. Je ne sais pas pourquoi le contenu du FBO devient alors zéro alors que que j'ai supprimé aussi glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    J'appelle _pickMap.init(_screenWidth, _screenHeight) depuis la fonction resizeGL() de ma classe principale.
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    bool PickingTexture::init(unsigned int WindowWidth, unsigned int WindowHeight)
       {
       initializeOpenGLFunctions();
     
       /* Creation du handle sur le nouveau frame buffer object (FBO) pour le picking */
       glGenFramebuffers(1, &_fbo);
       /* Rend m_fbo le frame buffer courant à la place de celui par défaut */
       glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
     
       /* Creation d'une texture OpenGL pour être un buffer d'information des primitives */
       glGenTextures(1, &_pickingTexture);
       glBindTexture(GL_TEXTURE_2D, _pickingTexture);
       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, WindowWidth, WindowHeight, 0, GL_RGB, GL_FLOAT, NULL);
       glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _pickingTexture, 0);
     
       /* Creation d'une texture OpenGL pour être un buffer de la profondeur (depth buffer) - je ne sais pas si c'est la peine de faire ça pour mon besoin */
       glGenTextures(1, &_depthTexture);
       glBindTexture(GL_TEXTURE_2D, _depthTexture);
       glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
       glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0);
     
       /* ...pour éviter un bug sur certaines vieilles CG */
       glReadBuffer(GL_NONE);
     
       glDrawBuffer(GL_COLOR_ATTACHMENT0);
     
       /* Le FBO s'est-il correctement initialisé ? */
       GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
       if (status != GL_FRAMEBUFFER_COMPLETE) {
          logError("GL", "PICKING::FRAMEBUFFER::INCOMPLETE") << status;
          return false;
       }
     
       /* Restore le framebuffer par défaut */
       glBindTexture(GL_TEXTURE_2D, 0);
       glBindFramebuffer(GL_FRAMEBUFFER, 0);
     
       return true;
        }
    Pourquoi dans la fonction resizeGL() : pour deux raisons. D'une part je ne connais pas encore les dimensions de la fenêtre lors de l'appel à initializeGL(), j'avais fais la bourde la première fois, mes paramètres WindowWidth et WindowHeight n'étaient pas initialisés, OpenGL m'a insulté . La fonction de resizeGL() étant appelée avant le premier paintGL() je pense que ce n'est pas une mauvaise chose. Mais aussi car je me dis que plus tard il faudra que je change la taille de la texture au redimensionnement de la fenêtre. PickingTexture.init() changera alors de nom pour plus de clarté. En attendant je conçois que redimensionner la fenêtre donnera des décalages dans la textures.

    Sur ce coup là, je doute que le problème provienne d'une mauvaise utilisation de Qt. Je pensais qu'en résolvant le problème du FBO, le retour du picking se serait amélioré. Ce qui n'est pas la cas.
    Je pense que j'ai dû faire une erreur pour le remplissage de mon FBO de picking.
    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
    21
     
    void Display::paintGL(void)
    {
        glBindVertexArray(this->_VAO);
     
        /* 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);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebufferObject());
     
        /* Display phase */
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        _displayShader->use();
        _camera.attach(*_displayShader, _screenWidth, _screenHeight);
        glDrawArrays(GL_TRIANGLES, 0, (_peptide->getVerticesArraySize()) / 6);
     
        glBindVertexArray(0);
    }
    Encore merci Jbx 2.0b pour ton aide sur le binding du FBO.

    Quelqu'un a-t-il une idée sur le pourquoi de mon second problème ?
    Merci d'avance !

  4. #4
    Invité
    Invité(e)
    Par défaut
    J'ai une piste.
    Si je commente glReadBuffer(GL_NONE) dans la fonction d'initialisation de la PickingTexture, le premier clic me donne bel et bien le retour voulu (1.0, 0.0, 0.0). Les clics suivants continuent de me fournir trois zéros.
    J'ai également supprimé un appel à glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) qui restait a cet endroit.

    Dans le tutoriel d'OGL il était dit que cette ligne servait a éviter certains bugs sur de vieilles cartes graphiques.

  5. #5
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Par défaut
    J'ai cherché un peu mais je vois rien qui me choque; J'suis pas vraiment un habitué du picking par couleur (plutôt du picking par scenegraph + lancer de rayon et mieux + octree).
    Mais juste pour te dire que resizeGL() a des chances d'être appelée plusieurs fois (même si ta fenêtre n'est pas redimensionnable, j'ai pu remarquer que les méthode resizeEvent & resizeGL étaient parfois appelées plusieurs fois au démarrage). Et donc que tu dois faire attention de recycler ton fbo et ta texture (glDeleteFramebuffers & glDeleteTextures) pour pas faire exploser la mémoire graphique.
    Enfin j'imagine que tu y avais peut-être pensé, mais on sait jamais

    Pour le reste je vois pas trop. Je comprends pas vraiment l'utilité d'un glReadBuffer(GL_NONE) au passage.

  6. #6
    Invité
    Invité(e)
    Par défaut
    A force de changer deux ou trois petits détails, je finis par avoir un picking presque fonctionnel. Mais je dois avouer ne pas être suffisamment à l'aise pour comprendre ce qui se passe.
    Quand je clique sur un objet, j'obtiens sa couleur. Problème, c'est la couleur de l'objet dans le back buffer quej'obtiens, et pas celui de mon fbo personnel.
    Quand je bind mon framebuffer et que je fais un glClearColor(0.0, 0.0, 0.0, 1), c'est le fond du backbuffer change.
    On dirait que le fait de binder mon propre FBO n'a aucun effet.

    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
        void Display::paintGL(void)
        {
            glBindVertexArray(this->_VAO);
     
            /* Picking phase */
            _pickingShader->use();
            _camera.attach(*_pickingShader, _screenWidth, _screenHeight);
            _pickMap.enableWriting();
            glClearColor(0.0, 0.0, 0.0, 1); // Pourquoi ça ne change pas la couleur de fond de _fbo uniquement ?
            glDrawArrays(GL_TRIANGLES, 0, (_peptide->getVerticesArraySize()) / 6);
     
            /* Display phase */
            glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject()); /* Warning : Qt's default frame buffer is not 0, use defaultFramebufferObject() instead */
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            _displayShader->use();
            _camera.attach(*_displayShader, _screenWidth, _screenHeight);
            glDrawArrays(GL_TRIANGLES, 0, (_peptide->getVerticesArraySize()) / 6);
     
            glBindVertexArray(0);
        }
     
        void Display::mousePressEvent(QMouseEvent *event)
        {
            if (event->buttons() & Qt::RightButton)
            {
                auto test = _pickMap.readPixel(event->x(), _screenHeight - event->y());
     
                qDebug() << test.ObjectID << test.DrawID << test.PrimID;
                            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebufferObject());
            }
        }
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
        bool PickingTexture::init(unsigned int WindowWidth, unsigned int WindowHeight)
        {
            initializeOpenGLFunctions();
     
            /* Creation du handle sur le nouveau frame buffer object (FBO) pour le picking */
            glGenFramebuffers(1, &_fbo);
            /* Rend m_fbo le frame buffer courant à la place de celui par défaut */
            glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
     
            /* Creation d'une texture OpenGL pour être un buffer d'information des primitives */
            glGenTextures(1, &_pickingTexture);
            glBindTexture(GL_TEXTURE_2D, _pickingTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, WindowWidth, WindowHeight, 0, GL_RGB, GL_FLOAT, NULL);
            glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _pickingTexture, 0);
     
            /* Creation d'une texture OpenGL pour être un buffer de la profondeur (depth buffer) */
            glGenTextures(1, &_depthTexture);
            glBindTexture(GL_TEXTURE_2D, _depthTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
            glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0);
     
            /* ...pour éviter un bug sur certaines vieilles CG */
            //glReadBuffer(GL_NONE);
     
            glDrawBuffer(GL_COLOR_ATTACHMENT0);
     
            /* Le FBO s'est-il correctement initialisé ? */
            GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
            if (status != GL_FRAMEBUFFER_COMPLETE) {
                logError("GL", "PICKING::FRAMEBUFFER::INCOMPLETE") << status;
                return false;
            }
     
            /* Restore le framebuffer par défaut */
            glBindTexture(GL_TEXTURE_2D, 0);
     
            return true;
        }
     
        void PickingTexture::enableWriting(void)
        {
            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo);
        }
     
        PickingTexture::PixelInfo PickingTexture::readPixel(unsigned int x, unsigned int y)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
            glReadBuffer(GL_COLOR_ATTACHMENT0);
     
            PixelInfo Pixel;
            glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &Pixel);
     
           //glReadBuffer(GL_NONE);
     
            return Pixel;
        }
     
        PickingTexture::PixelInfo::PixelInfo(void)
        {
            ObjectID = 0.0f;
            DrawID   = 0.0f;
            PrimID   = 0.0f;
        }
    Commenter la ligne
    _displayShader->use();
    appellée depuis PaintGL() me donne le bon résultat dans la console au clic de la souris. Mais en contrepartie j'affiche aussi les mauvaises couleurs, celles issues de mon shader de picking. C'est pourquoi j'ai l'impression que tout se joue dans le back buffer, comme si je n'arrivais pas à alterner entre mon _fbo et le framebuffer par défaut.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Mise en place d'un algorithme de color picking
    Par GLDavid dans le forum OpenGL
    Réponses: 46
    Dernier message: 26/08/2008, 16h25
  2. coloration syntaxique sous spe
    Par nico_h dans le forum EDI/RAD
    Réponses: 6
    Dernier message: 19/05/2008, 22h41
  3. Coloration syntaxique sous VIM/GVIM
    Par Neuromancien2 dans le forum Applications et environnements graphiques
    Réponses: 3
    Dernier message: 31/12/2007, 20h40
  4. [3.1.2.][Plugin][WebTools]La coloration syntaxique sous Eclipse
    Par Alexandre T dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 04/04/2007, 16h18
  5. Coloration syntaxique sous Visual C++
    Par chris_wafer_2001 dans le forum MFC
    Réponses: 1
    Dernier message: 25/09/2005, 10h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo