Pour le rendu voxel, quoi de plus efficace: un VBO pré-fait ou un shader de géométrie?

26

Étant donné un tableau de voxels assez statique, ce qui est plus efficace: utiliser le CPU pour pré-générer un VBO pour rendre les faces du voxel (en ignorant les formes de rendu plus avancées comme les cubes de marche pour l'instant) ou utiliser un shader de géométrie sur le GPU pour générer le visages à la volée?

Je ne suis pas si inquiet à propos de la mise à jour des voxels changeants, mais bien sûr, c'est un avantage de la version GPU puisque vous n'avez pas à reconstruire les VBO. De plus, l'approche GS semble un peu plus moderne :)

D'un autre côté, je n'ai pas examiné les détails sur la façon dont un GS fonctionne réellement avec le pipeline de rastérisation dans les GPU modernes. Sort-il les sommets dans une sorte de cache de flux ou les sommets sont-ils écrits dans la mémoire GPU normale entre les deux? Si c'est le dernier, alors la génération à la volée pourrait réduire la bande passante disponible et la puissance de traitement du reste des tâches GPU, je suppose, et il serait plus avantageux de le faire sur le CPU.

Bjorn Wesen
la source

Réponses:

9

Je pense à une scène de type minecraft, où par voxel vous voulez dire un monde de blocs qui sont réellement rendus à l'aide de polygones:

Si vous utilisez un shader de géométrie, il sera difficile d'éviter d'avoir exactement trois faces (ou autre) par voxel.

Si vous avez beaucoup de blocs adjacents qui ont la même texture, vous pouvez utiliser le pavage des textures pour avoir beaucoup moins de triangles dans votre bande (dégénérée) dans une approche VBO. Je veux dire, s'il y a une belle grande zone plate 6x6 de voxels d'herbe, vous pouvez dessiner tout le haut en seulement 2 triangles plutôt que 64.

Avec l'approche GS, vous ne pouvez pas non plus effectuer l'abattage trivial de visages occlus par des voxels adjacents, ce qui est très simple avec une approche VBO.

Je n'ai pas essayé l'approche GS, mais je peux dire que l'approche VBO avec la combinaison de répétition de tuiles adjacentes fonctionne très bien. J'ai trouvé que jouer avec les indices des éléments était beaucoup plus lent que de simplement répéter les sommets. Si vous divisez votre monde en beaux petits cubes, vous pouvez généralement utiliser un seul octet par composant par sommet et même emballer les informations de texture et les normales (une face sur un cube aligné sur l'axe n'a que 3 normales possibles), etc. dans un quatrième octet à faire 4 octets par sommet, ce qui est agréable et rapide.

J'ai utilisé des VBO séparés pour chacun des 6 visages - vous n'avez besoin que de dessiner au plus 3 d'entre eux évidemment. Cela correspond bien aux différentes textures habituellement utilisées sur les parties supérieures des voxels de style minecraft. Parce que pour chaque ensemble, la normale et telle est alors uniforme.

Avec l'utilisation de pixmaps en mosaïque verticale dans un atlas avec GL_REPEATsur l'axe horizontal et ayant des versions tournées à 90 degrés des pixmaps dans le même atlas, j'ai trouvé que je peux dessiner des quantités massives de blocs apparemment différents en utilisant le même VBO dans le même appel. Dans l'exemple de l'herbe 6x6, j'aurais divisé cela en 12 triangles car je n'ai répété qu'une seule dimension dans mon atlas.

J'ai surtout fait fonctionner cela sur le très bas de gamme des puces graphiques intégrées et du mobile, où GS est juste quelque chose avec lequel je peux rêver un jour de jouer.

Volonté
la source
3
Vous avez seulement besoin de dessiner au plus 3 faces par voxel, mais vous devrez peut-être dessiner des visages différents pour chaque voxel en fonction du point de vue, donc l'optimisation n'est pas si simple, non? Un VBO pré-fabriqué contiendra plus d'un voxel. Si votre point de vue est entre les voxels, vous verrez le côté est de l'un et le côté ouest de l'autre. La seule façon dont cela aiderait est que vous pouvez abattre trivialement les faces réelles tournées vers l'arrière, mais le pire des cas est toujours de rendre 5 des 6 côtés dans un groupe de voxels. Si votre point de vue est en dehors des limites axiales du VBO, il vous suffit de rendre 3 côtés.
Bjorn Wesen
Spot sur Bjorn, c'est faisable. (Mais je crée des VBO pour les blocs si nécessaire et reconsidère ce que j'ai construit lorsque la caméra bouge, plutôt que d'avoir le monde entier dans les VBO à tout moment; j'ai donc un temps naturel pour faire ces choix)
Will
10

Qu'en est-il de la troisième option, en utilisant des tableaux instanciés? Fondamentalement, vous dessinez de nombreuses boîtes (faites d'un simple cube à 8 sommets) avec un seul appel de tirage, en recherchant les positions (et d'autres données) en tant qu'attributs par instance à partir du VBO voxel-data (en utilisant glVertexAttribDivisorOpenGL, je suis sûr DX a ça aussi). Cela pourrait être plus rapide que l'approche des shaders de géométrie bien que le code d'application (non-shader) devrait être assez similaire, car je me souviens des shaders de géométrie ayant la réputation d'être lent, bien que je n'ai aucune expérience avec eux (ou instanciation) car je suis toujours assis sur le matériel 2.1.

Mais de toute façon, les shaders de géométrie ou les tableaux instanciés devraient être plus adaptés que la géométrie de voxel construite par CPU, en particulier lorsque les données de voxel sont sujettes à changement. En conjonction avec la rétroaction de transformation (sortie de flux en DX?), Vous pourrez peut-être configurer une bonne technique de suppression basée sur GPU.

Chris dit de réintégrer Monica
la source
Oui, c'est la meilleure solution à ce problème. Pourquoi ne m'est-il pas venu à l'esprit? :)
Notabene
Après quelques expérimentations, je dois vous dire que la géométrie cuite bat toute instanciation par une large marge. Je n'ai pas encore essayé les shaders de géométrie.
Jari Komppa
@JariKomppa pouvez-vous expliquer ce que vous entendez par géométrie cuite?
Steven Lu
Instances prétraduites et copiées dans un seul maillage. Comme avoir un maillage qui représente une centaine de cubes ou autre chose.
Jari Komppa
@JariKomppa J'ai vu les mêmes résultats, où la création du maillage est beaucoup plus rapide. Cependant, sur le gtx 680, l'option d'instanciation semble fonctionner beaucoup plus rapidement, bizarrement.
Levi H
1

La version Geometry Shader me semble beaucoup mieux. Vous ne pouvez avoir qu'un point vbo et une boîte de construction à la volée (point d'entrée, flux triangulaire de sortie). Ce sera rapide (encore plus rapide si vous utilisez l'unité de pavage dans le shader modèle 5 éq. DX11) et réduira considérablement la bande passante, ce sera une solution agréable et propre.

À propos de GS. Il est placé entre le vertex shader et le pixel shader et modifie le flux de vertex (primitives) en sortie. Alors que le vertex shader fonctionne uniquement sur les sommets, le geometry shader fonctionne sur des primitives entières. La sortie de ce flux va uniquement au pixel shader (et est tramée avant cela bien sûr :)) et il n'y a aucun moyen de l'enregistrer. (Peut-être par un rendu fou à la texture, puis une analyse syntaxique ... mais pas de possibilité vraiment simple)

Note sur les performances: vous devriez pouvoir tout faire dans le shader de géométrie et sauter (juste passer des données) vertex shader. Mais ce n'est pas le meilleur moyen. Mieux (plus rapidement) consiste à effectuer la plupart des transformations possibles sur le vertex shader et à essayer de minimiser le programme de géométrie shader. N'ayez pas peur d'utiliser pour le cycle si vous en aurez besoin (pour la création de box par exemple). Le compilateur le déroulera pour vous.

Notabene
la source
2
Il peut être judicieux de vérifier les voxels adjacents dans la géométrie et / ou le vertex shader et de supprimer les sommets ou d'ignorer les faces si elles sont occluses. Sinon, la solution GS augmentera la bande passante utilisée à la place.
Tamschi
La bande passante ne sera pas un gros problème (d'après mes expériences), mais bien sûr, c'est vrai. Et vous ne pouvez pas rechercher dans les autres primitives de GS (est-ce que je sais :)).
Notabene
@Tamschi: oui, ce problème m'est apparu juste après avoir écrit cette question .. pour la version CPU, les voxels au milieu des solides sont supprimés, mais cela pourrait être impossible sur le GPU sans un pré-passage avec ce qui équivaudrait à un différenciation ..
Bjorn Wesen
1
Vous pouvez lier le tampon de vertex à un uniforme isamplerBuffer ou usamplerBuffer dans le shader, puis effectuer des recherches avec une texture (name_of_uniform, index). Une autre option serait de lier le tampon à un tableau uniforme, ce qui vous donne plus de liberté dans le format de sommet que vous souhaitez utiliser.
Tamschi