Partage
  • Partager sur Facebook
  • Partager sur Twitter

[openGL]Vertex Array et .obj

    30 avril 2008 à 10:26:15

    Bonjour,

    Je me suis lancé récemment dans openGL, et j'ai encore du mal à assimiler certaines choses. Actuellement j'ai récupéré le contenu des lignes démarrant par v dans un vector<float>. A côté du loader j'ai un petit main.cpp qui me sert à tester l'avancée de la chose et à debugger. Travaillant le plus souvent en déplacement par ci par là je travaille sur mon portable qui ne supporte pas les VBO. J'utilise donc les VA pour afficher mon objet.

    Les présentations étant faites, venons en au problème.

    Dans mon exemple actuel, j'utilise un simple cube fait sous blender et exporté en .obj donc. En voici le contenu :

    # Blender v242 OBJ File: untitled.blend
    # <lien url="www.blender3d.org">www.blender3d.org</lien>
    mtllib cube.mtl
    o Cube_Cube
    v 0.768828 1.053423 -1.074781
    v 0.768828 -0.946577 -1.074781
    v -1.231172 -0.946577 -1.074781
    v -1.231171 1.053423 -1.074781
    v 0.768829 1.053423 0.925219
    v 0.768828 -0.946578 0.925219
    v -1.231172 -0.946577 0.925219
    v -1.231171 1.053423 0.925219
    usemtl Material
    s off
    f 5 1 8
    f 1 4 8
    f 3 7 8
    f 3 8 4
    f 2 6 3
    f 6 7 3
    f 1 5 2
    f 5 6 2
    f 5 8 7
    f 5 7 6
    f 1 2 3
    f 1 3 4


    Si j'avais fait la meme chose à coup de glVertex*() il m'aurait fallu 12 étapes de 3 lignes ( ce sont que des triangles, donc 3 sommets par face ). Donc, je suis persuadé que j'ai besoin de l'information des faces, donc des lignes démarrant par f. Cependant je ne sais pas comment les exploiter. Comment dire à openGL que je veux afficher les vertex 5 1 et 8 pour le premier triangle, puis 1 4 et 8 pour le second etc .. ?

    Si quelqu'un pouvait m'aider. J'ai lu et relu le tutoriel de Yno concernant les VA et VBO, mais je n'arrive pas à trouver l'information.

    Petit HS: Je cherche également un ouvrage correct sur openGL, les VA, les VBO, et qui, si possible mette en rapport les formats répandus comme le .obj etc.

    Bonne journée à tous.
    • Partager sur Facebook
    • Partager sur Twitter
      30 avril 2008 à 11:57:16

      Il s'agit des indices.

      Il suffit de remplacer la fonction d'affichage par:
      glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, mesIndices);

      Où mesIndices est un tableau.



      Ceci dit, les indices du format OBJ sont différent de ceux d'OpenGL. Tu n'auras pas de problème si tu n'utilises que les coordonnées des points. Mais des que tu ajoutera les normales et les textures, ça va être différent.

      En effet, avec OBJ on se retrouve à avoir la liste des coordonnées 3D, des normales, des coordonnées de textures, et les indices.

      Exemple:

      v 0.3445 4.459 9.458 // coordonnées 3D
      ...

      vn 0.5 1.0 0.3 // coordonnées d'une normale
      ...

      vt 0.5 0.4 // coordonnées de textures
      ...

      f 1/2/5 2/4/9 3/6/1 // une face => 3 sommets

      En fait, quand on regarde la composition d'un sommet en OBJ, on a 3 indices: 1/2/5
      1 => le premier indice pour les coordonnées 3D
      2 => deuxième indice pour la normale
      3 => l'indice pour les coordonnées de textures


      Or, le format OpenGL, c'est un seul est unique indice. Un indice pour un sommet.
      Et un sommet, c'est l'ENSEMBLE des coordonnées 3D, des normales, et des textures

      On aurait donc
      sommet 1 : v 0.3445 4.459 9.458 vn 0.5 1.0 0.3 vt 0.5 0.4
      sommet 2 : v 0.7363 2.974 4.847 vn 0.2 0.5 0.7 vt 0.3 0.8
      ...

      Et après, notre tableau d'indices:
      face 1 = sommet 1 + sommet 2 + sommet 5
      ...


      Voilà, la mission de ton loader, c'est de pouvoir passer du format OBJ au format OpenGL :)
      • Partager sur Facebook
      • Partager sur Twitter
        30 avril 2008 à 14:05:40

        Citation : Sebajuste

        Voilà, la mission de ton loader, c'est de pouvoir passer du format OBJ au format OpenGL :)


        D'ailleurs ce genre d'algo devient long à exécuter pour les modèles de plus de 1mo en obj, car pour chaque sommet il faut voir s'il existe déjà. Si quelqu'un connaît une forme "optimisée" de l'algo je suis preneur.
        • Partager sur Facebook
        • Partager sur Twitter
          30 avril 2008 à 17:24:29

          Et donc, comment j'exploite ces indices ? Je vais les faire un par un je pense, donc déjà les indices des vertex, car j'ai bien compris que les 8 lignes qui commencent par v représentent les positions des 8 coins de mon cube. Cependant, dans mon tableau, ( que ce soit un VA ou VBO apparement les tableaux sont les memes ( je me trompe ? ).

          Donc, j'explique ce que je pense avoir compris :

          -Je dois récuperer les indices ( avant le premier '/' donc )
          -J'insere dans un tableau tampon mes sommets
          -Puis dans le tableau final je vais chercher les vertex du tableau tampon à l'indice ( -1 puisque le premier vertex sera d'indice 0, donc j'interprete le 1 des indices comme le 0 de mon tableau tampon ).

          Donc dans le cas de mon .obj, j'aurai pour la premiere surface :
          tableau[0] = 0.768829; //Les trois coordonnées du 5è vertex ( puisque l'indice de la surface est signalé par "f 5 1 8"
          tableau[1] = 1.053423;
          tableau[2] = 0.925219;
          tableau[3] = 0.768828; //Les coordonnées du 1è vertex
          tableau[4] = 1.053423;
          tableau[5] = -1.074781;
          tableau[6] = -1.231171; //Les coordonnées du 8è vertex
          tableau[7] = 1.053423;
          tableau[8] = 0.925219;
          //Suite du tableau contenant les 11 faces restantes ..
          


          Ainsi je recupere le tableau de vertex, j'ai bon ?

          edit: En fait je viens de relire la derniere partie du cours sur les VA ( merci Yno ). Et apparement de ce que je comprends, on peut créer un tableau d'indices qui permettra à openGL de savoir quels sommets afficher, et dans quel ordre. Je vais tenter dans ce sens là, je vous tiens au courant.

          re-édit: J'ajoute le code source correspondant, car je ne comprend vraiment pas pourquoi je ne peux pas l'afficher.

          ObjLoader *loader = new ObjLoader();
              loader->selectFile("obj_files/cube.obj");
              loader->loadFile();
          
              bool running = true;
          
              SDL_Event evt;
          
              SDL_Init(SDL_INIT_VIDEO);
              SDL_SetVideoMode(640,480,16, SDL_OPENGL);
              SDL_WM_SetCaption("ObjLoader testing .. ", NULL);
          
              //cout << glGetString(GL_EXTENSIONS) << endl;
          
          
              glMatrixMode(GL_PROJECTION);
              glLoadIdentity();
              gluPerspective(70,(double)640/480,1,1000);
              glEnable(GL_DEPTH_BUFFER_BIT);
              glMatrixMode(GL_MODELVIEW);
              glLoadIdentity();
          
              gluLookAt(12,16,8,0,0,0,0,0,1);
          
              while(running)
              {
                  SDL_PollEvent(&evt);
          
          
                  if(evt.type == SDL_QUIT)
                      running = false;
          
                  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          
          
                  glEnableClientState(GL_VERTEX_ARRAY);
                  glVertexPointer(3, GL_FLOAT, 0, loader->getVertexTab());
          
                  glDrawElements(GL_TRIANGLES, loader->getFaceTab()->size(), GL_UNSIGNED_BYTE, loader->getFaceTab());
          
                  glDisableClientState(GL_VERTEX_ARRAY);
          
                  glFlush();
                  SDL_GL_SwapBuffers();
              }
              // ...
          


          Pour expliquer le fonctionnement rapide, j'instancie mon loader, je choisis mon .obj, puis je le charge, ce qui a pour simple effet en fait de remplir mes deux tableaux ( un contenant les vertex, un contenant les indices ).

          Mon cube ( vu les coordonnées ) etant plus ou moins centré sur l'origine je place la caméra à distance raisonnable pour être sur de l'avoir dans mon champs de vision, en pointant donc sur l'origine (0,0,0). Ensuite dans la boucle j'active les VA, je donne l'adresse de mon tableau de vertex à manger à glVertexPointer(), et je fournit mon tableau d'indice ( getFaceTab() ) à glDrawElements();

          A la sortie de la boucle je delete le loader etc évidemment. Les commentaires dans le code sont inexistants, j'ai preféré commenté à part, tout simplement car il n'y a pas de commentaire, ce code me sert donc juste à tester le fonctionnement du tout.

          Notez que j'ai pensé à verifier les valeurs de mes tableaux, et tout est bien ou il faut apparement, autrement dis, j'ai 24 cases dans le tableau de vertex ( 8 sommets x 3 coordonnées ) et 36 dans le tableau des indices ( donc 12 faces x 3 sommets ). Les valeurs sont bien dans l'ordre du .obj. Je peux donner la sortie du cout si il faut, mais ca n'apporterait pas grand chose je pense.

          Donc ça doit sans doute être une erreure d'inatention dissimulée quelque part, à moins que je n'ai toujours pas tout compris.
          • Partager sur Facebook
          • Partager sur Twitter
            1 mai 2008 à 10:08:16

            si tu veux pas géré les texture et les normale alors tu stock les indice en fessant -1a jaque valeur que tu a dans les f
            • Partager sur Facebook
            • Partager sur Twitter
              1 mai 2008 à 12:31:01

              Oui, c'est ce que j'ai fait jusque là. Et en intégrant les textures (par la suite), quelle va être la différence majeure ?

              En fait, il faut trier les tableaux de sorte à avoir la meme position pour chaque tableau, j'ai bon ? Autrement dis, les premieres cases du tableau de positions correspondent aux premieres du tableau couleur et aux premieres du tableau de normales etc.
              • Partager sur Facebook
              • Partager sur Twitter
                3 mai 2008 à 9:55:01

                Bon, j'vais décomposer la procédure :

                Un sommet = une séquence a/b/c (tu remarqueras qu'une ligne commençant par f en contient en général trois ou quatre (triangle ou quadrilatère)).
                OpenGL veut des indices de sommet, il faut donc pour chaque sommet créer un indice et un vertex. Mais si le sommet a déjà été créé, on ne duplique que l'indice et non les données du sommet.

                - soit un tableau d'entiers tabobj contenant tous
                  les indices obj de façon contigu;
                - soit un tableau de flottants posobj contenant toutes les données
                  de position des sommets (v) du fichier .obj;
                - soient texobj et norobj des tableaux identiques au précédent pour
                  les coordonnées de texture (vt) et les normales (vn);
                - soit un tableau d'entiers non signés finalindex de taille non
                  déterminable (choisis celle des indices obj/3, ça sera suffisamment
                  grand) qui contiendra les indices finaux, lisibles par OpenGL;
                - soit newindex un entier non signé placé à 0;
                - soient pos, tex, nor les tableaux flottants contenant les données
                  finales des données de sommet, lisibles par OpenGL;
                
                pour chaque sommet de tabobj (i=0; i<tabobj_num_variables; i+=3):
                  récupérer vertexindex = {tabobj[i], tabobj[i+1], tabobj[i+2]};
                  vérifier dans le tableau tabobj de 0 à i si la séquence vertexindex existe déjà;
                  si oui:
                    copier l'indice de finalindex correspondant dans finalindex[i/3];
                  si non:
                    finalindex[i/3] = newindex++;
                    pos[i] = posobj[tabobj[i]];
                    tex[i] = texobj[tabobj[i+1]];
                    nor[i] = norobj[tabobj[i+2]];


                Voici en gros l'algo pour générer des indices valides pour OpenGL.
                • Partager sur Facebook
                • Partager sur Twitter
                  3 mai 2008 à 10:49:32

                  Oui, effectivement, comme tu disais, ca doit être assez couteux en ressources cette génération d'indices. Notamment la partie qui consiste à vérifier si on a déjà enregistré un sommet. Car au final, avant d'insérer un sommet, on les reparcourt tous ( ou au moins jusqu'a ce qu'on l'ait trouvé ) puisqu'avant de finir le parcourt on ne sait pas si il y est. J'aimerais bien trouver une solution moins couteuse au chargement. Tu mets combien de temps pour charger un .obj de 1mo avec cet algo ?

                  Et pour finir, quelle est la différence fondamentale entre indice / pas indice. Actuellement je fais le travail inverse du tiens si on veut, un peu comme Skypers, j'utilise les indices pour ordonner mes 3 tableaux, que je bind pour le rendu. En somme, est-ce qu'il y a une grosse différence de performance ? si oui pourquoi ?
                  • Partager sur Facebook
                  • Partager sur Twitter
                    3 mai 2008 à 14:17:05

                    Citation : askerat

                    Oui, effectivement, comme tu disais, ca doit être assez couteux en ressources cette génération d'indices. Notamment la partie qui consiste à vérifier si on a déjà enregistré un sommet. Car au final, avant d'insérer un sommet, on les reparcourt tous ( ou au moins jusqu'a ce qu'on l'ait trouvé ) puisqu'avant de finir le parcourt on ne sait pas si il y est. J'aimerais bien trouver une solution moins couteuse au chargement. Tu mets combien de temps pour charger un .obj de 1mo avec cet algo ?

                    Et pour finir, quelle est la différence fondamentale entre indice / pas indice. Actuellement je fais le travail inverse du tiens si on veut, un peu comme Skypers, j'utilise les indices pour ordonner mes 3 tableaux, que je bind pour le rendu. En somme, est-ce qu'il y a une grosse différence de performance ? si oui pourquoi ?


                    Pour 1mo je crois que je met une seconde (avec un AMD Sempron 2200+ @ 1.8GHz). À savoir qu'un mesh d'1mo est déjà un mesh relativement conséquent, et qu'en 3D temps réel tu auras rarement à en charger de plus gros.
                    Il faut dire aussi que la vitesse dépend pas mal de l'implémentation de l'algo : évites l'ajout d'éléments à des vector C++, je "crois" qu'ils font une réallocation selon le cas, ce qui fait perdre énormément de temps, essayes plutôt de "prédire" la taille de tes tableaux, quitte à créer des tableaux de la bonne taille à la fin de l'algo si nécessaire, puis copier les données dedans (c'est comme ça que je fais).

                    Oui, l'utilisation d'indices pour le rendu augmente considérablement les performances. Elle permet l'utilisation du cache vertex de la carte graphique et limite le poids des données stockées en VRAM (mieux vaut stocker un int X fois qu'un sommet X fois). De plus lorsque tu voudras animer ton modèle, non seulement tu auras moins de données à transférer lors de la mise à jour, mais aussi moins de calculs à faire puisque quasiment aucun duplicata de position de sommet n'existera. En bref, c'est tout bénèf.
                    • Partager sur Facebook
                    • Partager sur Twitter
                      3 mai 2008 à 14:42:41

                      Concernant les performances de l'algorithme, c'est simple: Pour chaque nouveau vertex, il faut faire une recherche parmi tous ceux qu'on a déjà ajouté. Donc une approche "naive" (mais probablement pas loin de ce qu'on peu faire de mieux, d'un point de vu théorique) nous amène à faire n*(n+1)/2 comparaisons. Donc une complexité en O(n²). Maintenant, Comme le concept se rapproche assez d'un algorithm de tri (du point de vue des opération à effectuer), le mieux qui se fait actuellement, c'est du O(n*log(n)). Ce qui est mieux que O(n²). Mais je pense que les efforts pour atteindre cette complexité ne valent pas la peine. C'est même possible que le résultat soit plus lent que l'algorithme naïf, pour des collections de données petites.

                      En plus, si on prend en compte les normales, par défault Blender les calculent en style "flat shading", ce qui fait que 99.99% des sommets (donc position+normale+uv+...) sont uniques. Ce qui fait tomber l'algorithm dans un pire-cas, et pour finir avec autant de sommets qu'il n'y a d'indices.

                      En bref: pour les perfs, le .obj, y'a rien de pire ;)
                      • Partager sur Facebook
                      • Partager sur Twitter
                        3 mai 2008 à 16:06:07

                        Citation : Gravstein

                        En plus, si on prend en compte les normales, par défault Blender les calculent en style "flat shading"


                        Bah, ça n'a aucun rapport là... osef de ce que fait Blender, c'est le modeleur qui défini le lissage de son mesh. Avec Blender, un ptit clic sur Set Smooth et hop, le tour est joué.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          13 mai 2008 à 18:27:52

                          Je déterre le topic, car entre temps j'ai implémenter l'algorithme. Cependant j'ai un petit soucis avec glDrawElements(). Tout fonctionnait bien, mon algorithme tournait bien jusqu'à ce que je transferes le programme sur mon portable ( j'ai developpé ces fonctions durant le week end sur mon PC fixe ). Pour de petits meshs, je n'ai pas de soucis, cependant des que le mesh devient conséquent ( j'en ai un de 2.7mo sous la main ), j'ai une erreure de segmentation à l'appel de glDrawElements, j'ai verifié mes tableaux, j'ai bien une taille de 15112 ( j'ai plus la valeur exacte ) pour mon tableau de positions, et le plus grand indice ( calculé avec une simple boucle qui retourne la plus grande valeur du tableau ) est 15111, ce qui colle puisque je démarre à 0. De plus, mon tableau d'indices a une taille de 85110 ( mettons ), ce qui collait avec un grep | wc -l ( sous la console linux ) ou je donnais en parametre du grep le generique qui va bien. Ce sont les memes valeurs ( notament sur la dimension ) qui sont donnés à opengl. J'ai également vérifié les 30-40 premieres et dernieres entrées de mes tableaux, tout concorde avec le .obj.

                          Seulement sur un PC ça tourne, et pas sur l'autre, qu'est ce qui pourrait faire ça ? ( J'utilise des VA, pas de VBO ni rien qui fasse appel à des extensions quelquoncques. ) J'ai un pentium 1.2GHz avec 512Mo de ram, une carte intel 930 à peu pret reconnue par le systeme, et les deux PCs sont sous linux.

                          La grande différence à noter se trouve peut être dans le fais que mon portable tourne sous debian stable avec glibc 2.3 contre glibc2.7 sur le fixe qui tourne sous kubuntu.

                          Pour conclure, mes tableaux ont l'air correct, le code est le meme sur les deux tests, j'ai juste recompilé entre les deux essais ( version de glibc oblige ). Donc si quelqu'un a une idée d'ou ça pourrait venir ça m'arrangerait, car j'avoue avoir beaucoup cherché, j'ai revu la logique de mes algos, il est très fortement réductible et on peut améliorer la lisibilité, mais sur le papier il devrait tourner ( il tourne meme en pratique sur 1 PC sur 2 ^^ ).

                          Bonsoir ! ( bisous à mon futur sauveur ! )
                          • Partager sur Facebook
                          • Partager sur Twitter
                            13 mai 2008 à 21:53:19

                            Les vertex array ne fonctionnent que sur OpenGL 1.4 minimum. En dessous, c'est crash assuré si tu les utilises.

                            Vérifie que les pilotes (et donc la version d'OpenGL est à jour).

                            Pour garder la compatibilité avec les versions antérieur à OpenGL 1.4, je n'ai rien trouvé de mieu que tester la version d'OpenGL à l'exécution avec glGetString(GL_VERSION) et de passer aux vieux glBegin() glEnd() si la version est trop ancienne.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              13 mai 2008 à 22:37:47

                              Citation : Sebajuste

                              Pour garder la compatibilité avec les versions antérieur à OpenGL 1.4, je n'ai rien trouvé de mieu que tester la version d'OpenGL à l'exécution avec glGetString(GL_VERSION) et de passer aux vieux glBegin() glEnd() si la version est trop ancienne.


                              À ta place je vérifierais l'existence de l'extension nommée GL_EXT_vertex_array, c'est plus rigoureux.
                              • Partager sur Facebook
                              • Partager sur Twitter
                                13 mai 2008 à 22:44:01

                                Un de ces jours, faudrait que je consulte la liste complète des extensions existantes...

                                Merci Yno :p
                                • Partager sur Facebook
                                • Partager sur Twitter

                                [openGL]Vertex Array et .obj

                                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                • Editeur
                                • Markdown