web-dev-qa-db-fra.com

Quel est l'état de l'art pour le rendu du texte dans OpenGL à partir de la version 4.1?

Il existe déjà un certain nombre de questions sur le rendu du texte dans OpenGL, telles que:

Mais ce qui est surtout discuté, c'est le rendu des quads texturés à l'aide du pipeline à fonction fixe. Les shaders doivent sûrement faire mieux.

L'internationalisation ne me préoccupe pas vraiment, la plupart de mes chaînes seront des étiquettes de ticks d'intrigue (date et heure ou purement numériques). Mais les tracés seront restitués à la fréquence de rafraîchissement de l'écran et il pourrait y avoir beaucoup de texte (pas plus de quelques milliers de glyphes à l'écran, mais suffisamment pour que la présentation à accélération matérielle soit de Nice).

Quelle est l'approche recommandée pour le rendu de texte en utilisant OpenGL moderne? (Citer un logiciel existant en utilisant l'approche est une bonne preuve que cela fonctionne bien)

  • Les shaders de géométrie qui acceptent, par exemple, position et orientation, séquence de caractères et émission de quads texturés
  • Ombres de géométrie qui rendent les polices vectorielles
  • Comme ci-dessus, mais en utilisant plutôt des shaders de pavage
  • Un compute shader pour faire le rasterization de police
191
Ben Voigt

Le rendu des contours, à moins que vous ne rendiez le total que pour une douzaine de caractères, reste "interdit" en raison du nombre de sommets nécessaires par caractère pour se rapprocher de la courbure. Bien que des approches aient été utilisées pour évaluer les courbes de Bézier dans le pixel shader, celles-ci souffrent du manque d'antialiasing, ce qui est trivial avec un quad texturé avec une carte de distance, et l'évaluation des courbes dans le shader est toujours beaucoup plus onéreuse en termes de calcul.

Le meilleur compromis entre "rapide" et "qualité" reste les quads texturés avec une texture de champ de distance signée. C'est très légèrement plus lent que d'utiliser un quad texturé normal, mais pas autant. La qualité, en revanche, est dans un stade totalement différent. Les résultats sont vraiment époustouflants, ils sont aussi rapides que possible et des effets tels que la lueur sont trivialement faciles à ajouter. En outre, si nécessaire, la technique peut être rétrogradée sur un matériel plus ancien.

Voir le célèbre papier de valve pour la technique.

La technique est conceptuellement similaire au fonctionnement des surfaces implicites (metaballs 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 de la texture en tant que fonction de distance. Tout ce qui dépasse le seuil choisi (généralement 0,5) est "in", tout le reste est "out". Dans le cas le plus simple, sur du matériel non compatible avec les shaders âgé de 10 ans, la définition du seuil de test alpha à 0,5 fera exactement la même chose (sans effets spéciaux ni antialiasing).
Si on veut ajouter un peu plus de poids à la police (faux gras), un seuil légèrement inférieur fera l'affaire sans modifier une seule ligne de code (il suffit de changer votre uniforme "font_weight"). Pour un effet de rayonnement, on considère simplement tout ce qui se situe au-dessus d'un seuil comme "entrant" et tout ce qui se trouvant au-dessus d'un autre seuil (plus petit) comme étant "éteint, mais brillant" et les LERP entre les deux. L'anticrénelage fonctionne de la même manière.

En utilisant une valeur de distance signée de 8 bits plutôt qu’un seul bit, cette technique augmente de 16 fois la résolution effective de votre carte de texture dans chaque dimension (au lieu du noir et blanc, toutes les nuances possibles sont utilisées; nous avons donc 256 fois la informations utilisant le même stockage). Mais même si vous grossissez bien au-delà de 16x, le résultat semble tout à fait acceptable. Les longues lignes droites finiront par devenir un peu sinueuses, 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 en dehors des points (réduire la bande passante du bus), mais honnêtement, les gains sont plutôt marginaux. Il en va de même pour le rendu de caractère instancié décrit dans GPG8. Les frais généraux d’instanciation ne sont amortis que si vous avez un texte à lot. Les gains ne sont, à mon avis, aucunement liés à la complexité et à la non-dégradabilité ajoutées. De plus, vous êtes limité par la quantité de registres constants ou vous devez lire à partir d'un objet tampon de texture, ce qui n'est pas optimal pour la cohérence du cache (l'objectif était d'optimiser pour commencer!).
Un simple et ancien tampon de sommets ancien est tout aussi rapide (voire plus rapide) si vous planifiez le téléchargement un peu à l’avance et s’exécutera 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 à restituer.

Si vous êtes certain de ne pas avoir plus de 256 caractères dans votre police, vous pouvez envisager de dépouiller la bande passante du bus de la même manière que pour générer des quadruples à partir de points du shader de géométrie. Lors de l'utilisation d'une texture de tableau, les coordonnées de texture de tous les quads ont des coordonnées identiques _ et constantes s et t et ne diffèrent que par la coordonnée r qui est égale à l'index de caractère. rendre.
Mais comme pour les autres techniques, les gains attendus sont marginaux au point d’être incompatibles avec le matériel de la génération précédente.

Jonathan Dummer est un outil pratique pour générer des textures de distance: page de description

Mise à jour:
Comme indiqué plus récemment dans Extraction de sommets programmable (D. Rákos, "OpenGL Insights", p. 239), il n’existe pas de temps de latence supplémentaire ni de temps système associé au tirage de vertex. données programmées à partir du shader sur les dernières générations de GPU, par rapport à la même chose avec la fonction fixe standard.
De plus, les dernières générations de GPU ont de plus en plus de caches L2 polyvalents de taille raisonnable (par exemple, 1536kiB sur nvidia Kepler); la texture étant moins un problème.

Cela rend l’idée d’extraire des données constantes (telles que les tailles de quad) d’une texture 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:

  • Transférez uniquement un index de caractères (un par caractère à afficher) en tant que seule entrée vers un vertex shader qui transmet cet index et gl_VertexID, et amplifie cela jusqu'à 4 points dans le shader de géométrie, en conservant toujours l'index de caractère et l'id de vertex (ce sera "gl_primitiveID disponible dans le vertex shader") comme attributs uniques, et capturera cela via le retour de transformation.
  • Ce sera rapide, car il n'y a que deux attributs de sortie (principal goulot d'étranglement dans GS), et il est proche de "non-op" sinon dans les deux étapes.
  • Liez une texture tampon qui contient, pour chaque caractère de la police, les positions des sommets du quadrilatère texturé par rapport au point de base (il s’agit essentiellement des "métriques de police"). Ces données peuvent être compressées à 4 nombres par quad en ne stockant que le décalage du sommet en bas à gauche et en codant la largeur et la hauteur de la boîte alignée sur l'axe (en supposant que les demi-flottants, cela correspond à 8 octets de mémoire tampon constante par caractère - une police typique de 256 caractères pourrait s’intégrer complètement dans 2ko de cache L1).
  • Définir un uniforme pour la ligne de base
  • Lier une texture tampon avec des décalages horizontaux. Ceux-ci pourraient probablement même calculés sur le GPU, mais il est beaucoup plus facile et plus efficace pour ce genre de chose sur le processeur, car il s'agit d'une opération strictement séquentielle et pas du tout triviale (pensez de crénage). En outre, il faudrait une autre passe de retour, qui serait un autre point de synchronisation.
  • Pour restituer les données générées précédemment à partir de la mémoire tampon de retour, le vertex shader extrait le décalage horizontal du point de base et les décalages des sommets d'angle des objets tampon (à l'aide de l'id primitif et de l'index de caractère). L'ID de sommet original des sommets soumis est maintenant notre "ID primitif" (rappelez-vous que la GS a transformé les sommets en quadruples).

Ainsi, on pourrait idéalement réduire de 75% (amorti) la bande passante requise au sommet, bien qu’elle ne puisse restituer qu’une seule ligne. Si l'on veut pouvoir rendre plusieurs lignes en un seul appel, il faut ajouter la ligne de base à la texture de la mémoire tampon, plutôt que d'utiliser un uniforme (ce qui réduit les gains de bande passante).

Cependant, même en supposant une réduction de 75% - puisque les données de sommet pour afficher des quantités de texte "raisonnables" se situent aux alentours de 50-100 koB (ce qui est pratiquement zéro vers un GPU ou un bus PCIe ) - Je doute toujours que la complexité ajoutée et la perte de compatibilité ascendante en valent vraiment la peine. Réduire zéro de 75% n’est toujours que zéro. J'ai certes pas essayé l'approche ci-dessus, et il faudrait plus de recherche pour faire une déclaration vraiment nuancée. Néanmoins, à moins que quelqu'un 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 reste que pour les données de sommet, un tampon de vertex simple et ancien est assez bon à juste titre. être considéré comme faisant partie d'une "solution à la pointe de la technologie". C'est simple et direct, ça marche, et ça marche bien.

Ayant déjà référencé " OpenGL Insights " ci-dessus, il convient également de souligner le chapitre "Rendu 2D de formes par champs de distance" de Stefan Gustavson qui explique le rendu de champs distants en détail.

Mise à jour 2016:

Parallèlement, il existe plusieurs techniques supplémentaires visant à éliminer les artefacts de contournement des angles qui deviennent gênants à 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 à un imaginaire ligne dépassant du 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: détails et implémentation disponible sur github . Il s’agit d’une amélioration par rapport aux outils utilisés précédemment pour résoudre le problème. Bonne qualité, légèrement, presque pas, lent, mais utilise trois fois plus de mémoire de texture. De plus, les effets supplémentaires (par exemple, une lueur) sont plus difficiles à obtenir.

Enfin, stocker les courbes de Bézier constituant les personnages et les évaluer dans un fragment shader est devenu pratique , avec des performances légèrement inférieures (mais pas tant que cela pose problème) et des résultats stupéfiants, même à fort grossissement .
Démo WebGL rendant un grand PDF avec cette technique en temps réel disponible ici .

194
Damon

http://code.google.com/p/glyphy/

La principale différence entre GLyphy et les autres moteurs de rendu OpenGL basés sur SDF est que la plupart des autres projets échantillonnent le SDF dans une texture. Cela a tous les problèmes habituels que l'échantillonnage a. C'est à dire. il déforme le contour et est de mauvaise qualité. GLyphy représente plutôt le SDF en utilisant les vecteurs réels soumis au GPU. Il en résulte un rendu de très haute qualité.

L'inconvénient est que le code est pour iOS avec OpenGL ES. Je vais probablement créer un port OpenGL 4.x sous Windows/Linux (j'espère que l'auteur ajoutera de la documentation, cependant).

14
Display Name

La technique la plus répandue est toujours les quads texturés. Cependant, en 2005, LORIA a mis au point un système appelé textures vectorielles, c’est-à-dire en rendant les graphiques vectoriels sous forme de textures sur des primitives. Si vous utilisez ceci pour convertir les polices TrueType ou OpenType en une texture vectorielle, vous obtenez ceci:

http://alice.loria.fr/index.php/publications.html?Paper=VTM@2005

12
datenwolf

Je suis surpris que le bébé de Mark Kilgard, NV_path_rendering (NVpr), n'ait été mentionné par aucun des précédents. Bien que ses objectifs soient plus généraux que le rendu des polices, il peut également restituer le texte à partir de polices et avec le crénage. Cela n’a même pas besoin d’OpenGL 4.1, mais c’est pour le moment une extension réservée aux vendeurs/à Nvidia. Il transforme les polices en chemins en utilisant glPathGlyphsNV qui dépend de la bibliothèque freetype2 pour obtenir les métriques, etc. Vous pouvez également accéder aux informations de kerning avec glGetPathSpacingNV et utiliser le mécanisme de rendu du chemin général de NVpr pour afficher texte de l'utilisation des polices "converties" du chemin. (Je mets cela entre guillemets, car il n'y a pas de vraie conversion, les courbes sont utilisées telles quelles.)

Le démo enregistrée pour les capacités de police de NVpr n'est malheureusement pas particulièrement impressionnant. (Peut-être que quelqu'un devrait en faire un dans le sens de la démo beaucoup plus éclatante de SDF on peut en trouver sur les intertubes ...)

La présentation de l’API 2011 NVpr pour la partie polices commence ici et se poursuit dans la partie suivante ; il est un peu malheureux que cette présentation soit divisée.

Plus de matériaux généraux sur NVpr:

  • Nvidia NVpr hub , mais certains éléments de la page de destination ne sont pas les plus récents.
  • papier Siggraph 2012 pour les cerveaux de la méthode de rendu de chemin, appelée "pochoir, puis couvrez" (StC); Le document explique également brièvement le fonctionnement de technologies concurrentes telles que Direct2D. Les bits relatifs aux polices ont été relégués à ne annexe du document . Il y a aussi quelques extras comme des vidéos/démos .
  • présentation GTC 2014 pour un statut de mise à jour; en un mot: il est maintenant pris en charge par Skia de Google (Nvidia a fourni le code fin 2013 et 2014), qui est à son tour utilisé dans Google Chrome et [indépendamment de Skia, je pense] dans un version bêta d'Adobe Illustrator CC 2014
  • la documentation officielle dans le registre d'extension OpenGL
  • L’USPTO a accordé au moins quatre brevets à Kilgard/Nvidia en relation avec NVpr, dont vous devriez probablement être au courant, si vous souhaitez implémenter StC par vous-même: S8698837 , S8698808 , S87048 et S873025 . Notez qu’il existe quelque 17 autres documents USPTO liés à cette publication en tant que "également publiés en tant que", la plupart d’entre eux étant des demandes de brevet. Il est donc tout à fait possible que davantage de brevets soient octroyés à partir de ceux-ci.

Et comme le mot "pochoir" n'a généré aucun succès sur cette page avant ma réponse, il apparaît que le sous-ensemble de la communauté SO qui a participé à cette page dans la mesure où, malgré son assez grand nombre, n'était pas au courant Kilgard a un message de type FAQ sur le forum opengl qui peut illustrer en quoi les méthodes de rendu de chemin sans tessellation sont différentes. à partir de graphiques 3D standard standard, même s'ils utilisent encore un GPU [GP] (NVpr nécessite une puce compatible CUDA).

Du point de vue historique, Kilgard est également l'auteur du classique "Une API basée sur OpenGL simple pour le texte texturé", SGI, 1997 , à ne pas confondre avec le NVpr basé sur le gabarit qui a fait ses débuts 2011.


La plupart, sinon toutes les méthodes récentes discutées sur cette page, y compris les méthodes basées sur le gabarit telles que NVpr ou les méthodes basées sur SDF telles que GLyphy (que je ne discuterai plus ici parce que d'autres réponses le couvrent déjà) ont cependant une limitation: elles sont: convient aux grands écrans de texte conventionnels (~ 100 DPI) sans décalage, quel que soit leur niveau, et leur apparence est agréable, même en petite taille, sur des écrans de type rétine à haute résolution. Cependant, ils ne fournissent pas complètement ce que Microsoft Direct2D + DirectWrite vous donne, à savoir des allusions de petits glyphes sur des écrans traditionnels. (Pour un aperçu visuel des suggestions en général, voir cette page de typothèque par exemple. Une ressource plus détaillée est sur antigrain.com .)

Je ne suis au courant d'aucun élément basé sur OpenGL, ouvert et productif, capable de faire ce que Microsoft peut laisser pour le moment. (J'admets l'ignorance vis-à-vis des éléments internes OS X GL/Quartz d'Apple, car à ma connaissance Apple n'a pas publié comment ils procèdent au rendu des polices/chemins d'accès basé sur GL. Il semble que OS X, contrairement à MacOS 9, ne fait aucune allusion, ce qui agace certaines personnes .) Quoi qu'il en soit, il y a n article de recherche de 2013 qui traite de l'allusion via les shaders OpenGL écrit par Nicolas P. Rougier de l'INRIA; il vaut probablement la peine d'être lu si vous devez faire allusion à OpenGL. Même s'il peut sembler qu'une bibliothèque comme freetype fait déjà tout le travail quand il s'agit de faire allusion, ce n'est pas le cas pour la raison suivante: Je cite le journal:

La bibliothèque FreeType peut pixelliser un glyphe en utilisant un anti-aliasing de sous-pixels en mode RVB. Cependant, cela ne représente que la moitié du problème, car nous souhaitons également obtenir un positionnement sous-pixel pour un placement précis des glyphes. L'affichage du quadrilatère texturé à des coordonnées de pixel fractionnaires ne résout pas le problème, car il en résulte une interpolation de texture au niveau du pixel entier. Au lieu de cela, nous souhaitons obtenir un décalage précis (entre 0 et 1) dans le domaine des sous-pixels. Cela peut être fait dans un fragment shader [...].

La solution n'est pas vraiment triviale, je ne vais donc pas essayer de l'expliquer ici. (Le document est en libre accès.)


Une autre chose que j'ai apprise dans le document de Rougier (et que Kilgard ne semble pas avoir pris en compte) est que les puissances de police utilisées (Microsoft + Adobe) ont créé non pas une mais deux méthodes de spécification de crénage. L'ancien est basé sur une soi-disant table kern et est supporté par freetype. Le nouveau s'appelle GPOS et n'est supporté que par les bibliothèques de polices les plus récentes telles que HarfBuzz ou pango dans le monde du logiciel libre. Étant donné que NVpr ne semble prendre en charge aucune de ces bibliothèques, le crénage pourrait ne pas fonctionner immédiatement avec NVpr pour certaines nouvelles polices; il y en a qui sont apparemment dans la nature, selon cette discussion de forum .

Enfin, si vous avez besoin de faire mise en page de texte complexe (CTL) vous ne semblez pas avoir de chance avec OpenGL car aucune bibliothèque basée sur OpenGL ne semble exister pour cela. (DirectWrite, d’autre part, peut gérer CTL.) Il existe des bibliothèques open source telles que HarfBuzz qui peuvent rendre CTL, mais je ne sais pas comment vous pourriez les faire fonctionner correctement (par exemple, en utilisant les méthodes basées sur le gabarit) via OpenGL. Vous devrez probablement écrire le code collé pour extraire les contours remodelés et les insérer dans des solutions NVpr ou SDF sous forme de chemins.

7
Fizz

Je pense que votre meilleur pari serait de regarder cairo graphics avec le backend OpenGL.

Le seul problème que j'ai rencontré lors du développement d'un prototype avec 3.3 core était l'utilisation de fonctions obsolètes dans le backend OpenGL. C'était il y a 1-2 ans, donc la situation aurait pu s'améliorer ...

Quoi qu'il en soit, j'espère qu'à l'avenir, les pilotes graphiques opengl implémenteront OpenVG.

3
Orhun