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)
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:
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.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 .
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).
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
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:
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.
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.