Le rendu des contours, à moins que vous ne rendiez qu'une douzaine de caractères au total, reste un «non» en raison du nombre de sommets nécessaires par caractère pour approximer la courbure. Bien qu'il y ait eu des approches pour évaluer les courbes de Bézier dans le pixel shader à la place, celles-ci souffrent de ne pas être facilement anti-crénelées, ce qui est trivial en utilisant un quad text-map texturé, et l'évaluation des courbes dans le shader est toujours beaucoup plus coûteuse en calcul que nécessaire.
Le meilleur compromis entre «rapide» et «qualité» sont toujours des quads texturés avec une texture de champ de distance signée. Il est très légèrement plus lent que l'utilisation d'un quadruplet texturé normal, mais pas tellement. La qualité, d'autre part, est dans un tout autre stade. Les résultats sont vraiment étonnants, ils sont aussi rapides que possible et les effets tels que la lueur sont faciles à ajouter. En outre, la technique peut être facilement rétrogradée vers un matériel plus ancien, si nécessaire.
Voir le célèbre papier Valve pour la technique.
La technique est conceptuellement similaire au fonctionnement des surfaces implicites (métaboules et autres), bien qu'elle ne génère pas de polygones. Il fonctionne entièrement dans le pixel shader et prend la distance échantillonnée à partir de la texture comme une fonction de distance. Tout ce qui dépasse un seuil choisi (généralement 0,5) est "in", tout le reste est "out". Dans le cas le plus simple, sur un matériel non compatible avec les shaders vieux de 10 ans, la définition du seuil de test alpha à 0,5 fera exactement la même chose (mais sans effets spéciaux et anti-crénelage).
Si l'on veut ajouter un peu plus de poids à la police (faux gras), un seuil légèrement plus petit fera l'affaire sans modifier une seule ligne de code (il suffit de changer l'uniforme de "font_weight"). Pour un effet de lueur, on considère simplement tout ce qui est au-dessus d'un seuil comme "en" et tout ce qui est au-dessus d'un autre seuil (plus petit) comme "en dehors, mais en lueur", et les LERP entre les deux. L'anticrénelage fonctionne de manière similaire.
En utilisant une valeur de distance signée de 8 bits plutôt qu'un seul bit, cette technique augmente la résolution effective de votre texture map 16 fois dans chaque dimension (au lieu du noir et blanc, toutes les nuances possibles sont utilisées, ainsi nous avons 256 fois la informations utilisant le même stockage). Mais même si vous agrandissez bien au-delà de 16x, le résultat semble tout à fait acceptable. Les longues lignes droites finiront par devenir un peu ondulées, mais il n'y aura pas d'artefacts d'échantillonnage "en bloc" typiques.
Vous pouvez utiliser un shader de géométrie pour générer les quads à partir de points (réduire la bande passante du bus), mais honnêtement, les gains sont plutôt marginaux. La même chose est vraie pour le rendu de caractère instancié comme décrit dans GPG8. Les frais généraux d'instanciation ne sont amortis que si vous avez beaucoup de texte à dessiner. Les gains sont, à mon avis, sans rapport avec la complexité supplémentaire et la non-rétrogradation. De plus, vous êtes soit limité par la quantité de registres constants, soit vous devez lire à partir d'un objet tampon de texture, ce qui n'est pas optimal pour la cohérence du cache (et l'intention était d'optimiser pour commencer!).
Un tampon de vertex simple et simple est tout aussi rapide (peut-être plus rapide) si vous planifiez le téléchargement un peu à l'avance et fonctionnera sur tous les matériels construits au cours des 15 dernières années. Et, il n'est pas limité à un nombre particulier de caractères dans votre police, ni à un nombre particulier de caractères à rendre.
Si vous êtes sûr de ne pas avoir plus de 256 caractères dans votre police, les tableaux de texture peuvent valoir la peine d'être supprimés de la bande passante du bus de la même manière que la génération de quadruples à partir de points dans le shader de géométrie. Lors de l' utilisation d' une texture de matrice, les coordonnées de texture de toutes les quads ont, constante identique s
et les t
coordonnées et ne diffèrent que par la r
coordination, ce qui est égal à l'indice de caractère à rendre.
Mais comme avec les autres techniques, les gains attendus sont marginaux au prix d'être incompatibles avec le matériel de génération précédente.
Il y a un outil pratique par Jonathan Dummer pour générer des textures de distance: page de description
Mise à jour:
comme plus récemment souligné dans Vertex Pulling Programmable (D. Rákos, "OpenGL Insights", pp. 239), il n'y a pas de latence ou de surcharge supplémentaire significative associée à l'extraction de données de vertex par programme à partir du shader sur les dernières générations de GPU, par rapport à faire la même chose en utilisant la fonction fixe standard.
De plus, les dernières générations de GPU ont de plus en plus de caches L2 à usage général de taille raisonnable (par exemple 1536 ko sur nvidia Kepler), donc on peut s'attendre au problème d'accès incohérent lors de l'extraction de décalages aléatoires pour les coins du quadruple à partir d'une texture de tampon étant moins d'un problème.
Cela rend l'idée de l'extraction de données constantes (telles que les tailles quadruples) d'une texture de tampon plus attrayante. Une implémentation hypothétique pourrait ainsi réduire au minimum les transferts PCIe et mémoire, ainsi que la mémoire GPU avec une approche comme celle-ci:
- Téléchargez uniquement un index de caractères (un par caractère à afficher) en tant que seule entrée d'un vertex shader qui transmet cet index et
gl_VertexID
, et amplifiez cela à 4 points dans le geometry shader, ayant toujours l'index de caractère et l'id de vertex (ce sera "gl_primitiveID mis à disposition dans le vertex shader") en tant qu'attributs uniques, et capturez-le via un retour de transformation.
- Ce sera rapide, car il n'y a que deux attributs de sortie (goulot d'étranglement principal dans GS), et il est proche de "no-op" sinon dans les deux étapes.
- Liez une texture tampon qui contient, pour chaque caractère de la police, les positions des sommets du quadruple texturé par rapport au point de base (ce sont fondamentalement les "mesures de police"). Ces données peuvent être compressées à 4 chiffres par quad en stockant uniquement le décalage du sommet inférieur gauche et en codant la largeur et la hauteur de la boîte alignée sur l'axe (en supposant que les demi-flottants, ce sera 8 octets de tampon constant par caractère - une police typique de 256 caractères pourrait tenir complètement dans 2 Ko de cache L1).
- Définir un uniforme pour la ligne de base
- Liez une texture tampon avec des décalages horizontaux. Ceux - ci pourraient probablement même être calculés sur le GPU, mais c'est beaucoup plus facile et plus efficace pour ce genre de chose sur le CPU, car c'est une opération strictement séquentielle et pas du tout triviale (pensez au crénage). En outre, il aurait besoin d'une autre passe de rétroaction, qui serait un autre point de synchronisation.
- Rendez les données générées précédemment à partir du tampon de rétroaction, le vertex shader extrait le décalage horizontal du point de base et les décalages des sommets des coins à partir des objets tampon (en utilisant l'ID primitif et l'index de caractère). L'ID de sommet d'origine des sommets soumis est maintenant notre "ID primitif" (rappelez-vous que le GS a transformé les sommets en quads).
De cette façon, on pourrait idéalement réduire la bande passante du sommet requise de 75% (amorti), bien qu'il ne puisse rendre qu'une seule ligne. Si l'on voulait pouvoir rendre plusieurs lignes en un seul appel, il faudrait ajouter la ligne de base à la texture du tampon, plutôt que d'utiliser un uniforme (ce qui rend les gains de bande passante plus petits).
Cependant, même en supposant une réduction de 75% - puisque les données de sommet pour afficher des quantités de texte "raisonnables" ne se situent que autour de 50-100 ko (ce qui est pratiquement zéroà un GPU ou à un bus PCIe) - Je doute toujours que la complexité supplémentaire et la perte de rétrocompatibilité en valent vraiment la peine. Réduire le zéro de 75% n'est encore que zéro. Je n'ai certes pas essayé l'approche ci-dessus, et plus de recherches seraient nécessaires pour faire une déclaration vraiment qualifiée. Mais encore, à moins que quelqu'un ne puisse démontrer une différence de performances vraiment étonnante (en utilisant des quantités de texte "normales", pas des milliards de caractères!), Mon point de vue demeure que pour les données de vertex, un simple et ancien tampon de vertex simple est à juste titre assez bon à considérer comme faisant partie d'une "solution de pointe". C'est simple et direct, cela fonctionne et cela fonctionne bien.
Ayant déjà référencé " OpenGL Insights " ci-dessus, il convient également de souligner le chapitre "Rendu de forme 2D par champs de distance" de Stefan Gustavson qui explique le rendu des champs de distance de manière très détaillée.
Mise à jour 2016:
En attendant, il existe plusieurs techniques supplémentaires qui visent à supprimer les artefacts d'arrondi de coin qui deviennent perturbants à des grossissements extrêmes.
Une approche utilise simplement des champs de pseudo-distance au lieu de champs de distance (la différence étant que la distance est la distance la plus courte non pas au contour réel, mais au contour ou à une ligne imaginaire dépassant le bord). C'est un peu mieux et fonctionne à la même vitesse (shader identique), en utilisant la même quantité de mémoire de texture.
Une autre approche utilise la médiane de trois dans une texture à trois canaux et une implémentation disponible sur github . Cela vise à être une amélioration par rapport aux hacks et-ou utilisés précédemment pour résoudre le problème. Bonne qualité, légèrement, presque imperceptiblement, plus lent, mais utilise trois fois plus de mémoire de texture. De plus, les effets supplémentaires (par exemple la lueur) sont plus difficiles à obtenir correctement.
Enfin, le stockage des courbes de Bézier réelles constituant les personnages et leur évaluation dans un fragment shader est devenu pratique , avec des performances légèrement inférieures (mais pas tellement que c'est un problème) et des résultats étonnants, même avec des grossissements les plus élevés.
Démo WebGL rendant un grand PDF avec cette technique en temps réel disponible ici .