Comment la texturation virtuelle peut-elle être efficace?


27

Pour référence, ce à quoi je fais référence est le "nom générique" pour la première technique (je crois) introduite avec la technologie MegaTexture d' idTech 5 . Voir la vidéo ici pour un rapide coup d'œil sur son fonctionnement.

J'ai parcouru récemment des articles et des publications à ce sujet, et ce que je ne comprends pas, c'est comment cela peut être efficace. Ne nécessite-t-il pas un recalcul constant des coordonnées UV de l'espace "page de texture globale" en coordonnées de texture virtuelles? Et comment cela ne freine-t-il pas la plupart des tentatives de regroupement de la géométrie? Comment peut-il permettre un zoom arbitraire? Cela ne nécessiterait-il pas à un moment donné la subdivision des polygones?

Il y a tellement de choses que je ne comprends pas et je n'ai pas pu trouver de ressources réellement facilement accessibles sur le sujet.

Réponses:


20

Présentation

La principale raison de Virtual Texturing (VT), ou Sparse Virtual Textures , comme on l'appelle parfois, est comme une optimisation de la mémoire. L'essentiel de la chose est de ne déplacer dans la mémoire vidéo que les texels réels (généralisés en tant que pages / tuiles) dont vous pourriez avoir besoin pour une image rendue. Ainsi, il vous permettra d'avoir beaucoup plus de données de texture dans un stockage hors ligne ou lent (disque dur, disque optique, cloud) qu'il n'en aurait autrement sur la mémoire vidéo ou même la mémoire principale. Si vous comprenez le concept de mémoire virtuelle utilisé par les systèmes d'exploitation modernes, c'est la même chose dans son essence (le nom n'est pas donné par accident).

VT ne nécessite pas de recalculer les UV dans le sens où vous le feriez pour chaque image avant de rendre un maillage, puis soumettez à nouveau les données de sommet, mais cela nécessite un travail considérable dans les shaders Vertex et Fragment pour effectuer la recherche indirecte à partir des UV entrants. Dans une bonne implémentation cependant, elle devrait être complètement transparente pour l'application si elle utilise une texture virtuelle ou traditionnelle. En fait, la plupart du temps, une application mélangera les deux types de textures, virtuelle et traditionnelle.

Le traitement par lots peut en théorie très bien fonctionner, même si je n'ai jamais examiné les détails de cela. Étant donné que les critères habituels pour regrouper la géométrie sont les textures, et avec VT, chaque polygone de la scène peut partager la même texture "infiniment grande", théoriquement, vous pouvez obtenir un dessin de scène complet avec 1 appel de dessin. Mais en réalité, d'autres facteurs entrent en jeu, rendant cela impossible.

Problèmes avec VT

Le zoom avant / arrière et les mouvements brusques de la caméra sont les choses les plus difficiles à gérer dans une configuration VT. Cela peut sembler très attrayant pour une scène statique, mais une fois que les choses commencent à bouger, plus de pages / tuiles de texture seront demandées que vous ne pouvez en diffuser pour le stockage externe. Les entrées-sorties et les threads asynchrones peuvent aider, mais s'il s'agit d'un système en temps réel, comme dans un jeu, vous n'aurez qu'à effectuer le rendu pour quelques images avec des tuiles de résolution inférieure jusqu'à ce que celles en haute résolution arrivent, de temps en temps. , ce qui donne une texture floue. Il n'y a pas de solution miracle ici et c'est le plus gros problème avec la technique, l'OMI.

La texture virtuelle ne gère pas non plus la transparence de manière simple, les polygones transparents ont donc besoin d'un chemin de rendu traditionnel distinct pour eux.

Dans l'ensemble, VT est intéressant, mais je ne le recommanderais pas à tout le monde. Cela peut bien fonctionner, mais il est difficile à mettre en œuvre et à optimiser, et il y a juste trop de cas d'angle et de réglages spécifiques au cas nécessaires à mon goût. Mais pour les grands jeux en monde ouvert ou les applications de visualisation de données, cela pourrait être la seule approche possible pour adapter tout le contenu au matériel disponible. Avec beaucoup de travail, il peut être fait pour fonctionner assez efficacement même sur un matériel limité, comme nous pouvons le voir dans les versions PS3 et XBOX360 d'id's Rage .

la mise en oeuvre

J'ai réussi à faire fonctionner VT sur iOS avec OpenGL-ES, dans une certaine mesure. Mon implémentation n'est pas "livrable", mais je pourrais éventuellement le faire si je le voulais et si j'avais les ressources. Vous pouvez voir le code source ici , cela pourrait aider à avoir une meilleure idée de la façon dont les pièces s'assemblent. Voici une vidéo d'une démonstration exécutée sur iOS Sim. Il semble très lent car le simulateur est terrible pour émuler des shaders, mais il fonctionne bien sur un appareil.

Le diagramme suivant décrit les principaux composants du système dans mon implémentation. Il diffère un peu de la démo SVT de Sean (lien vers le bas), mais il est plus proche en architecture de celui présenté par le document Accelerating Virtual Texturing Using CUDA , trouvé dans le premier livre GPU Pro (lien ci-dessous).

système de texturation virtuel

  • Page Filessont les textures virtuelles, déjà découpées en tuiles (pages AKA) en tant qu'étape de prétraitement, elles sont donc prêtes à être déplacées du disque vers la mémoire vidéo chaque fois que cela est nécessaire. Un fichier d'échange contient également l'ensemble des mipmaps, également appelés mipmaps virtuels .

  • Page Cache Managerconserve une représentation côté application des textures Page Tableet Page Indirection. Comme le déplacement d'une page du stockage hors ligne vers la mémoire coûte cher, nous avons besoin d'un cache pour éviter de recharger ce qui est déjà disponible. Ce cache est un cache LRU ( Least Latest Used ) très simple . Le cache est également le composant responsable de la mise à jour des textures physiques avec sa propre représentation locale des données.

  • Il Page Providers'agit d'une file d'attente de travaux asynchrones qui récupérera les pages nécessaires pour une vue donnée de la scène et les enverra dans le cache.

  • La Page Indirectiontexture est une texture avec un pixel pour chaque page / tuile dans la texture virtuelle, qui mappera les UV entrants à la Page Tabletexture de cache qui contient les données de texel réelles. Cette texture peut devenir assez grande, elle doit donc utiliser un format compact, comme RGBA 8: 8: 8: 8 ou RGB 5: 6: 5.

Mais il nous manque encore un élément clé ici, et c'est comment déterminer quelles pages doivent être chargées du stockage dans le cache et par conséquent dans le Page Table. C'est là que le Feedback Pass et l' Page Resolverentrée.

Le Feedback Pass est un pré-rendu de la vue, avec un shader personnalisé et à une résolution beaucoup plus basse, qui écrira les identifiants des pages requises dans le tampon de cadre couleur. Ce patchwork coloré du cube et de la sphère ci-dessus sont des index de page réels encodés en couleur RGBA. Ce rendu pré-passe est ensuite lu dans la mémoire principale et traité par le Page Resolverpour décoder les index de page et déclencher les nouvelles requêtes avec le Page Provider.

Après le pré-passage Feedback, la scène peut être rendue normalement avec les shaders de recherche VT. Mais notez que nous n'attendons pas la fin de la nouvelle demande de page, ce serait terrible, car nous bloquerions simplement les fichiers synchrones IO. Les demandes sont asynchrones et peuvent être prêtes ou non au moment du rendu de la vue finale. S'ils sont prêts, doux, mais sinon, nous gardons toujours une page verrouillée d'un mipmap basse résolution dans le cache comme solution de rechange, nous avons donc des données de texture à utiliser, mais cela va être flou.

Autres ressources à vérifier

La VT est toujours un sujet un peu brûlant sur l'infographie, donc il y a des tonnes de bons documents disponibles, vous devriez pouvoir en trouver beaucoup plus. S'il y a autre chose que je peux ajouter à cette réponse, n'hésitez pas à demander. Je suis un peu rouillé sur le sujet, je n'ai pas beaucoup lu à ce sujet au cours de la dernière année, mais c'est toujours bon pour la mémoire de revisiter des trucs :)


Hé, merci pour l'excellente réponse. Je sais que cela est généralement mal vu, mais j'ai plusieurs problèmes, donc je passe juste la plupart du temps en revue les choses - pour avoir un aperçu intuitif des sujets pour l'avenir (j'ai bien peur que l'apprentissage et la mise en œuvre des choses soient hors de ma portée pour le moment ) - de toute façon, si possible, pourriez-vous publier un exemple de pseudocode décrivant le processus lui-même, idéalement, mais pas nécessairement, illustré?
Llamageddon

1
@Llamageddon, il se trouve que j'avais encore un diagramme à portée de main;) Je crains que le pseudo-code ne soit un peu difficile à fournir, car il contient un peu de vrai code. Mais j'espère que la réponse élargie aidera à donner une idée générale de la technique.
glampert

3
Il convient de noter que la plupart des matériels modernes exposent désormais des tables de pages programmables, éliminant ainsi le besoin d'une texture de redirection. Ceci est exposé par exemple par des ressources réservées directx12 , qui s'appuient sur des ressources carrelées directx11 , ou des textures clairsemées opengl .
MooseBoys

1
@Llamageddon, la pré-passe de rétroaction peut être effectuée à une résolution inférieure pour économiser autant de calcul et de mémoire que possible, car les pixels d'une page se répéteront généralement (vous pouvez remarquer les grands carrés colorés dans ma démo). Vous avez raison de penser qu'il pourrait éventuellement manquer une page visible comme celle-ci, mais cela n'aura généralement pas un impact visuel important, car le système devrait toujours conserver au moins la mipmap la plus basse de l'ensemble du VT disponible dans le cache. Ce deuxième article que j'ai lié a tous les exemples de shaders en annexe, vous pouvez également vous référer au dépôt pour mon propre projet, ils sont similaires.
glampert

1
@glampert Ahh, je vois; ça a du sens. Pourtant, je pense qu'il existe de nombreuses options pour gérer les transparents; dans le passage d'ID de page, vous pouvez tramer (de sorte que l'histogramme verrait toutes les pages, sauf s'il y avait un grand nombre de couches transparentes), ou utiliser une approche k-buffer , ou même simplement baser la résidence de texture transparente sur laquelle les objets sont proches de la appareil photo (par opposition à leur rendu dans une passe de rétroaction).
Nathan Reed

11

La texturation virtuelle est l'extrême logique des atlas de textures.


Un atlas de texture est une texture géante unique qui contient des textures pour des maillages individuels à l'intérieur:

Exemple d'atlas de texture

Les atlas de texture sont devenus populaires en raison du fait que le changement de textures provoque une vidange complète du pipeline sur le GPU. Lors de la création des maillages, les UV sont compressés / décalés afin qu'ils représentent la «partie» correcte de l'ensemble de l'atlas de texture.

Comme @ nathan-reed l'a mentionné dans les commentaires, l'un des principaux inconvénients des atlas de texture est de perdre les modes d'habillage tels que la répétition, le pincement, la bordure, etc. En outre, si les textures n'ont pas assez de bordure autour, vous pouvez accidentellement échantillon d'une texture adjacente lors du filtrage. Cela peut entraîner des artefacts de saignement.

Les Atlas de texture ont une limitation majeure: la taille. Les API graphiques imposent une limite souple sur la taille d'une texture. Cela dit, la mémoire graphique n'est que si grande. Il y a donc aussi une limite stricte sur la taille de la texture, donnée par la taille de votre v-ram. Les textures virtuelles résolvent ce problème, en empruntant des concepts à la mémoire virtuelle .

Les textures virtuelles exploitent le fait que dans la plupart des scènes, vous ne voyez qu'une petite partie de toutes les textures. Donc, seul ce sous-ensemble de textures doit être en vram. Le reste peut être dans la RAM principale ou sur disque.

Il existe plusieurs façons de le mettre en œuvre, mais j'expliquerai la mise en œuvre décrite par Sean Barrett dans son discours sur GDC . (que je recommande fortement de regarder)

Nous avons trois éléments principaux: la texture virtuelle, la texture physique et la table de recherche.

Texture virtuelle

La texture virtuelle représente le méga atlas théorique que nous aurions si nous avions assez de vram pour tout faire. Il n'existe en fait nulle part en mémoire. La texture physique représente les données de pixels que nous avons réellement en vram. La table de recherche est le mappage entre les deux. Pour plus de commodité, nous divisons les trois éléments en tuiles ou pages de taille égale.

La table de recherche stocke l'emplacement du coin supérieur gauche de la tuile dans la texture physique. Donc, étant donné un UV à la texture virtuelle entière, comment obtenir les UV correspondants pour la texture physique?

Tout d'abord, nous devons trouver l'emplacement de la page dans la texture physique. Ensuite, nous devons calculer l'emplacement des UV dans la page. Enfin, nous pouvons ajouter ces deux décalages ensemble pour obtenir l'emplacement des UV dans la texture physique

float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;


Calcul de pageLocInPhysicalTex

Si nous faisons de la table de recherche la même taille que le nombre de tuiles dans la texture virtuelle, nous pouvons simplement échantillonner la table de recherche avec l'échantillonnage du voisin le plus proche et nous obtiendrons l'emplacement du coin supérieur gauche de la page dans la texture physique.

float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);


Calculer inPageLocation

inPageLocation est une coordonnée UV relative à la partie supérieure gauche de la page plutôt qu'à la partie supérieure gauche de la texture entière.

Une façon de calculer cela est de soustraire les UV du coin supérieur gauche de la page, puis de les mettre à l'échelle de la taille de la page. Cependant, c'est un peu de maths. Au lieu de cela, nous pouvons exploiter la façon dont la virgule flottante IEEE est représentée. La virgule flottante IEEE stocke la partie fractionnaire d'un nombre par une série de fractions de base 2.

entrez la description de l'image ici

Dans cet exemple, le nombre est:

number = 0 + (1/2) + (1/8) + (1/16) = 0.6875

Voyons maintenant une version simplifiée de la texture virtuelle:

Texture virtuelle simple

Le 1/2 bit nous indique si nous sommes dans la moitié gauche de la texture ou dans la droite. Le quart de bit nous indique dans quel quart de la moitié nous sommes. Dans cet exemple, puisque la texture est divisée en 16, ou 4 de côté, ces deux premiers bits nous disent dans quelle page nous sommes. Le reste les bits nous indiquent l'emplacement à l'intérieur de la page.

Nous pouvons obtenir les bits restants en déplaçant le flotteur avec exp2 () et en les supprimant avec fract ()

float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);

Où numTiles est un int2 donnant le nombre de tuiles par côté de la texture. Dans notre exemple, ce serait (4, 4)

Calculons donc l'inPageLocation pour le point vert, (x, y) = (0.6875, 0.375)

inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
               = float2(0.6875, 0.375) * int2(2, 2);
               = float2(1.375, 0.75);

inPageLocation = fract(float2(1.375, 0.75));
               = float2(0.375, 0.75);

Une dernière chose à faire avant que nous ayons terminé. Actuellement, inPageLocation est une coordonnée UV dans «l'espace» de texture virtuelle. Cependant, nous voulons une coordonnée UV dans «l'espace» de texture physique. Pour ce faire, nous devons simplement mettre à l'échelle inPageLocation par le rapport entre la taille de la texture virtuelle et la taille de la texture physique

inPageLocation *= physicalTextureSize / virtualTextureSize;



La fonction terminée est donc:

float2 CalculatePhysicalTexUV(float2 virtTexUV, Texture2D<float2> lookupTable, uint2 physicalTexSize, uint2 virtualTexSize, uint2 numTiles) {
    float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);

    float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
    inPageLocation = fract(inPageLocation);
    inPageLocation *= physicalTexSize / virtualTexSize;

    return pageLocInPhysicalTex + inPageLocation;
}

Je ne le suis pas, je fais référence à la texturation virtuelle, plus connue sous le nom de technologie MegaTexture d' idTech 5 . Voir aussi ceci et cela . Je l'ai vu mentionné dans un aperçu des pipelines de rendu de nombreux moteurs modernes et dans quelques articles qui utilisent une approche similaire pour les shadowmaps. Il a beaucoup en commun avec les atlas de textures, oui, il les utilise en quelque sorte, mais je ne le confond pas avec les atlas de textures.
Llamageddon

Ahh. Merci pour les liens. Pouvez-vous les ajouter à la question. Je
mettrai à

3
IMO, le principal inconvénient des atlas de texture simples (et non des textures virtuelles) est que vous perdez les modes d'habillage comme la répétition et le pincement, et le saignement se produit en raison du filtrage / mipmapping - et non de la précision à virgule flottante. Je serais surpris de voir la précision du flotteur devenir un problème pour les textures ordinaires (non virtuelles); même une texture 16K (le maximum autorisé par les API actuelles) n'est pas assez grande pour vraiment forcer la précision du flotteur.
Nathan Reed

@RichieSams Btw, je pense que votre réponse est bonne, même si à une question différente. Vous devriez faire un post de questions et réponses.
Llamageddon

Hmm, cela explique assez bien, même si je ne comprends pas vraiment comment cela fonctionne avec les niveaux de mip. J'aimerais pouvoir écrire mon problème spécifique de compréhension, mais cela m'échappe un peu ...
Llamageddon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.