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

3D Java Discussion :

[LWJGL] Rendu d'un monde de voxels


Sujet :

3D Java

  1. #1
    Nouveau Candidat au Club
    [LWJGL] Rendu d'un monde de voxels
    Bonjour,

    j'essaye en ce moment de développer un système de rendu de monde de voxels ( comme Minecraft, je sais très original .. ) avec LWJGL et Opengl 3.3+

    Enfait, mon problème se situe dans le fait que je cherche à optimiser au maximum la vitesse de rendu : utiliser le moins de draw calls possible, et minimiser les transferts de données entre la carte graphique et le processeur.

    Mon système pour l'instant est simple : 3 threads :
    • Thread logique : mécanique principale
    • Thread graphique : appels Opengl, etc
    • Thread d'échange de données : informations sur les voxels


    Mes blocs sont stockés dans de grands tableaux d'entiers de 64*64*256 (appelés "chunks").

    Du coup, j'en suis venu à 2 idées de "structures" différentes pour le rendu, mais qui ont chacunes leurs inconvénients :
    • Première méthode : Pour chaque voxel de la map, je stocke le minimum d'informations nécessaires au rendu : id texture, niveau de luminosité, le tout dans un grand VBO. Si un bloc est vide ou ne doit pas être rendu, l'id de la texture est 0 (ceci afin de pouvoir midifier facilement le VBO plus tard, comme la place est deja réservée dans la mémoire).
      Ensuite, je créé 6 VAO contenant chacun 1 VBO contenant eux même les coordonnées des points d'une des 6 faces d'un cube. Puis, selon la direction et la position de la caméra, à chaque frame, je rends 3 de ces VBO ( on peut voir au maximum 3 faces d'un cube au même moment ) avec glDrawArraysInstanced. Les informations sur les blocs sont ainsi utilisées en paramètres : dans le vertex shader, si l'id de la texture est différent de 0, je rends le bloc, sinon, j'annule l'instance.

      Avantages :
      • je peux modifier facilement un bloc en "mappant" le VBO et en modifiant seulement les blocs concernés
      • un seul draw call, moins de synchronisations entre la carte graphique et le processeur


      Inconvénients :
      • si le bloc n'existe pas et qu'il ne doit pas être rendu, un vertex shader a quand même été lancé et cela coûte beaucoup en performances

    • Deuxième méthode : On reprend la méthode 1, mais on sépare chaque chunk dans un VBO différent. De plus, les blocs inexistants ne sont pas ajoutés ni remplacés par des 0.

      Avantages :
      • pas de vertex shaders lancés inutilement
      • frustum culling plus facile
      • moins de mémoire utilisée sur la carte graphique car les blocs vides ne sont pas enregistrés


      Inconvénients :
      • à chaque modification d'un chunk, il faut réuploader TOUTES les informations concernant ce chunk (mauvais pour les performances ..)
      • beaucoup plus de draw calls (64 pour un terrain de 256 * 256 * 256 blocs)



    Mes questions sont donc :

    1) Peut on annuler prématurément une instance selon une valeur stockée dans un VBO avec glDrawArraysInstanced ?
    2) Peut on ajouter ou supprimer des informations dans un VBO sans avoir à tout réuploader (ce qui peut être très fréquent) ?

    Merci d'avance si vous avez lu (et compris) mon pavé !

  2. #2
    Futur Membre du Club
    Salut !

    Pour répondre à ta deuxième question, il ne me semble pas que ce soit possible de modifier uniquement un partie spécifique d'un VBO.

    J'ai déjà fais un minecraft-like java et lwjgl et ma technique était d'avoir un VBO pour chaque chunk.
    Le frustum culling est beaucoup plus efficace et cela réduit pas mal le nombre de draw calls.
    Et si tu as des chunks "pleins", tu peux même faire de l'occlusion culling car tu es sur que le qu'on ne verra pas à travers le chunk.

    Et pour ta première question je suis justement entrain de tester l'instanced rendering (ma première fois) donc si je trouve des infos je reviendrais les poster.

    Hors-sujet: Ce serait possible de t'ajouter sur skype ? Je connais pas beaucoup de programmeurs Java+LWJGL français qui s'y connaissent autant que toi. Et je pense que ça serait sympa de pouvoir échanger/discuter.

  3. #3
    Nouveau Candidat au Club
    Salut !

    En effet j'ai fini par utiliser des VBO différents pour chaque chunk, ma première méthode était en fait tout à fait irréaliste Ce qui fait que j'ai même abandonné l'instanced rendering. Sauf que pas longtemps après avoir posté ce message, windows a décidé de formatter ma clé USB avec tout mon code dessus donc je n'ai plus rien . En plus avec le bac qui se prépare et tout j'ai plus le temps.

    Pour skype pas de problème mais je risque de pas souvent être connecté ^^ (je t'ai envoyé un MP)

    Ah oui et aussi :
    Je connais pas beaucoup de programmeurs Java+LWJGL français qui s'y connaissent autant que toi
    Je suis juste lycéen et je n'ai même jamais fait tourner autre chose qu'un triangle coloré avec LWJGL

  4. #4
    Futur Membre du Club
    Courte réponse
    GL15.glMapBuffer GL15.glBufferSubData, j'ai trouvé ces deux fonctions pour mettre à jour une partie seulement d'un vbo.

    PS: Pas le temps de faire une réponse totale, j'éditerais celle-ci quand j'aurai le temps.

    EDIT:

    Je suis juste lycéen et je n'ai même jamais fait tourner autre chose qu'un triangle coloré avec LWJGL
    Je suis aussi lycéen, et pour connaitre les vbos faut quand même s'intéresser pas mal à opengl je pense. Après c'est sur que OpenGL est très vaste.

    Pour skype pas de problème mais je risque de pas souvent être connecté ^^ (je t'ai envoyé un MP)
    Si tu préfère utiliser autre chose pour disctuer on peux utiliser ce que tu veux, c'est juste que je pense pas souvent à aller sur developpez.net.

  5. #5
    Nouveau Candidat au Club
    Effectivement glMapBuffer et glBufferSubData permettent de modifier une seule partie d'un VBO mais pas d'ajouter ou supprimer des points.

    Ce qui ramene a remplacer les blocs vides par des points qui ne seront pas rendus mais qui déclencherons quand même des shaders pour rien

    Du coup on est obligé de réuploader le VBO en entier a chaque fois ! Mais d'après mes tests ce n'est pas tellement un problème niveau performance (quand on voit que les anciennes versions d'openGL envoient tous les points a chaque frame tout de suite on relativise ..).

    Également le nombre de draw calls non plus n'est pas si important. Ce qu'il faut éviter c'est les binds / debinds de textures car ça change le contenu du cache de la carte graphique et cette opération est couteuse. Donc pour remédier à ça il faut faire des sprites.

  6. #6
    Futur Membre du Club
    Effectivement glMapBuffer et glBufferSubData permettent de modifier une seule partie d'un VBO mais pas d'ajouter ou supprimer des points.
    Parameters
    target
    Specifies the target buffer object.
    The symbolic constant must be GL_ARRAY_BUFFER,
    GL_ELEMENT_ARRAY_BUFFER,
    GL_PIXEL_PACK_BUFFER, or
    GL_PIXEL_UNPACK_BUFFER.

    offset
    Specifies the offset into the buffer object's data store where data replacement will begin, measured in bytes.

    size
    Specifies the size in bytes of the data store region being replaced.

    data
    Specifies a pointer to the new data that will be copied into the data store.
    Ce sont les paramètres de la fonction glBufferSubData() (source: http://www.opengl.org/sdk/docs/man2/...ferSubData.xml). Et le dernier paramètre data permet d'envoyer les nouvelles coordonnées des nouveaux points. Je ne comprends pas pourquoi tu dis que l'on ne peux pas en ajouter ou en supprimé.

    Ce qui ramene a remplacer les blocs vides par des points qui ne seront pas rendus mais qui déclencherons quand même des shaders pour rien
    Je me suis mal fait comprendre, je voulais dire qu'il faut utiliser ces deux fonctions avec ta deuxième méthode (Un vbo -> un Chunk).
    Car quand tu update un chunk la fonction glBufferData() recréer un espace mémoire pour le VBO déjà existant en d'autres termes c'est mauvais pour les perfs.

    When replacing the entire data store, consider using glBufferSubData rather than completely recreating the data store with glBufferData. This avoids the cost of reallocating the data store.
    source: http://www.opengl.org/sdk/docs/man2/...ferSubData.xml

    J'ai envoyé par erreur edit en cours.

  7. #7
    Nouveau Candidat au Club
    Je ne comprends pas pourquoi tu dis que l'on ne peux pas en ajouter ou en supprimé.
    Désolé je me suis mal exprimé Je voulais dire qu'on ne peut pas changer la taille d'un VBO avec glMapBuffer ou glBufferSubData, mais on peut effectivement changer les valeurs stockées dedans.

    Si mon VBO a 256 valeurs, je ne pourrais pas en mettre 260, il faudra que je réupload totalement le VBO et donc réallouer la mémoire. Par contre, si je dois changer les points 135, 136 et 137 et leur donner une autre valeur alors la je peux utiliser glBufferSubData.
    (c'est ce que j'ai compris en tout cas, dis moi si je me trompe)

    Dans le cas ou on utilise l'instanced rendering :
    Si je ne veux pas réallouer la mémoire à chaque fois il faut que mon VBO ai la taille maximale dès le début. Les cubes vides doivent quand même être représentés dans le VBO (par exemple avec un ID de bloque 0) et vont de toute façon lancer un shader pour rien vu qu'il n'y a rien à rendre pour ces bloques.

    Dans le cas ou on entre traditionnellement les coordonnées des points :
    Dans ce cas glBufferSubData est ce qu'il faut. On donne la taille maximale au VBO dès le début et au moment de rendre on ne rend pas le VBO entier, juste le nombre de bloques qu'il faut.

  8. #8
    Futur Membre du Club
    Désolé je me suis mal exprimé Je voulais dire qu'on ne peut pas changer la taille d'un VBO avec glMapBuffer ou glBufferSubData, mais on peut effectivement changer les valeurs stockées dedans.
    C'est ce que je me disais !

    Personnellement j'opterais pour la seconde option.

    Qu'est ce que tu compte faire ?

  9. #9
    Nouveau Candidat au Club
    Moi aussi la deuxième option me parait la plus viable, et c'est celle que je vais essayer d'implémenter. (quand j'aurais le temps ...) Je vais essayer aussi de garder l'instanced rendering.