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:
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.
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.
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:
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;
}