Est-il possible de construire un cube avec moins de 24 sommets

14

J'ai un monde basé sur des cubes comme Minecraft et je me demande s'il existe un moyen de construire un cube avec moins de 24 sommets afin de pouvoir réduire l'utilisation de la mémoire.

Cela ne me semble pas possible pour 2 raisons: les normales ne sortiraient pas bien et les textures par face ne fonctionneraient pas.

Est-ce le cas ou je me trompe? Peut-être existe-t-il une nouvelle technologie DX11 sophistiquée qui peut vous aider?

Edit: Juste pour clarifier, j'ai 2 exigences: j'ai besoin de normales de surface pour chaque face de cube afin de faire un bon éclairage et j'ai besoin d'un moyen d'adresser un index différent dans un tableau de texture pour chaque face de cube

Telanor
la source
1
Voulez-vous réduire l' utilisation de la mémoire de la carte graphique ou de la RAM ?
doppelgreener
1
Les données de sommet sont stockées dans la RAM et sur le GPU en ce moment, donc les réduire réduirait les deux.
Telanor

Réponses:

9

Si vous n'avez besoin que de normales par face et si vos texcoords pour une face sont strictement 0/0, 0/1, 1/0, 1/1 (ou similaire pour convenir à votre disposition), vous pouvez construire un cube avec 8 verts et soit 30 (bande avec redémarrage) ou 36 (liste) index. Récupérez les normales et les texcoords en utilisant une recherche de tableau constante basée sur SV_VertexID dans votre vertex shader.

Cela signifie que vous n'avez même pas besoin d'inclure des texcoords ou des normales dans votre tampon de vertex, ce qui vous donnera encore plus d'économie de mémoire.

En allant plus loin, vous pouvez toujours aller jusqu'à 24 verts par cube mais aussi utiliser l'instanciation. Chaque cube aurait une taille fixe dans votre tampon de vertex (1x1x1) et vous auriez un facteur d'échelle et une position (en supposant que vos cubes ne tournent pas, une matrice s'ils le font) en tant que données par instance. Dans le cas non rotatif, vous avez un coût unique de 24 verts, mais chaque cube n'a besoin que de 6 flotteurs pour être entièrement spécifié. Dans le cas rotatif, vous regardez 16 flottants, mais même cela représente une économie substantielle (vous êtes plus susceptible de goulot d'étranglement côté processeur sur les transformations matricielles dans ce cas - pour le cas non rotatif construisant une matrice à la volée) votre vertex shader - même si c'est fait par vertex, est si stupidement rapide que vous n'avez même pas besoin de vous en soucier).

Pour les textures par face, utilisez simplement un tableau de textures. Vous devez vous assurer que chacune de ces textures dans le tableau est de la même taille, bien sûr, et vous devrez toujours casser votre lot actuel si le tableau lui-même doit changer, mais sinon il fera très bien le travail. Ajoutez un troisième texcoord à votre définition de sommet qui définit la tranche de tableau à utiliser pour chaque face.

Vous n'avez pas besoin d'un GS avec cela, et il devrait fonctionner plus rapidement que d'en utiliser un car l'activation du stage de shaders de géométrie imposera des frais supplémentaires.

J'ai un code de référence dans mon moteur qui dessine juste un tas de cubes en utilisant cette méthode, et je peux facilement mâcher plus de 300 000 cubes tout en effaçant 60fps, sur un GPU relativement bas de gamme, et sans rien faire d'autre pour optimiser le processus . Certes, je ne les éclaire ni ne les texture, mais j'ai le mélange alpha activé, la suppression de la face arrière désactivée, et dans l'ensemble, il s'équilibre avec ma partie "ne rien faire d'autre pour optimiser", donc cela devrait vous donner une idée raisonnable du type de vous pouvez frapper avec cette méthode.

Maximus Minimus
la source
1
J'utilisais l'instanciation avant et cela fonctionne très bien pour les cubes de moins de 40k. Mais j'ai au moins 256k cubes, et l'instanciation ne le gérait pas aussi. L'utilisation de SV_VertexID avec une recherche semble cependant très prometteuse.
Telanor
Quelle était la taille de votre tampon par instance? Essayer de les bourrer tous dans un énorme tampon peut finir par étouffer votre GPU; Je garde généralement les tampons par instance à suffisamment d'espace pour les objets 64k (en divisant les appels de dessin si nécessaire), j'utilise le modèle discard / no-overwrite et j'écris directement sur le pointeur mappé au lieu de copier à partir d'un tableau intermédiaire, ce qui fonctionne très bien .
Maximus Minimus
De plus, il est essentiel de maintenir un faible nombre de cartes. Mapper / écrire un cube / démapper 40k fois sera beaucoup plus lent que mapper / écrire 40k cubes / démapper une seule fois. Si vous faisiez le premier, essayez de passer au second.
Maximus Minimus
15

Je pense que l'optimisation principale que vous pouvez faire est basée sur le fait que tous les cubes n'auront pas réellement besoin des 24 sommets . En fait, les seuls cubes qui ont besoin de 24 sommets sont ceux qui flottent dans les airs, ce qui est probablement rare.

En général, ne générez des quadruples que pour les faces en contact avec l'air . Cela signifie que si deux cubes se touchent, vous n'avez pas besoin de générer de sommets pour la face où ils se touchent.

L'image ci-dessous illustre ce concept, mais en 2D pour une meilleure compréhension. Dans l'image, il y a 11 blocs occupés (représentés par les cercles gris remplis), ce qui nécessiterait normalement 4 x 11 = 44 bords pour représenter (4 car c'est un carré, pas un cube). Mais comme vous pouvez le voir, vous n'avez vraiment besoin de dessiner les bords que lorsqu'ils sont en contact avec un carré vide, qui dans ce cas, n'est que de 8 bords.

entrez la description de l'image ici

Si un exemple aussi simple en 2D a réussi à réduire 44 arêtes à 8 arêtes, imaginez les gains dans un grand monde 3D ...

C'est l'approche décrite dans cet article , que je vous recommande de lire, bien qu'elle soit destinée à OpenGL. Les concepts devraient cependant être assez universels.

Vous pourriez aussi probablement utiliser un shader de géométrie pour générer les sommets à la volée sur le GPU, éliminant ainsi la nécessité de les stocker en mémoire, mais je n'ai pas d'expérience avec cela, et je ne sais pas non plus à quel point il fonctionnerait pour un grand monde.

David Gouveia
la source
1
Article très intéressant. Je fais déjà l'optimisation des bords, ce qui aide certainement beaucoup. Je vais essayer les shaders de géométrie et voir si cela fonctionne.
Telanor
Voulez-vous dire, utiliser le shader de géométrie et le produit croisé pour calculer les normales? Je pensais à l'utiliser, comme créer un programme séparé pour les surfaces planes (je ne suis pas concerné par les textures).
dreta
@dreta Non seulement cela, mais en allant au point où vous soumettez uniquement les centroïdes du cube en tant que liste de points, et tous les sommets sont émis par le GS. Peut-être encodez-vous des informations voisines dans les points afin que le shader ne génère que les visages vraiment nécessaires, même si je n'ai pas pensé comment.
David Gouveia
Si c'est la performance que vous voulez, n'utilisez pas un shader de géométrie ... Presque jamais. Ils ont complètement foiré le pipeline. Si vous souhaitez générer une géométrie sur le GPU, utilisez un shader de calcul ou openCL
Robert Fraser