Troika: Fonctionnalité : ajouter des attributs d'index de caractères/mots/lignes à la géométrie du texte

Créé le 11 févr. 2021  ·  18Commentaires  ·  Source: protectwise/troika

Les shaders personnalisés appliqués aux instances de texte pourraient permettre de très beaux effets d'animation. Pour beaucoup d'entre eux, vous voudriez traiter chaque caractère/glyphe indépendamment, et pour cela, vous avez besoin de quelque chose dans le shader vous indiquant quel caractère est actuellement rendu.

Théoriquement, gl_InstanceID pourrait être utilisé pour cela, puisque nous utilisons l'instanciation pour les quads de glyphes. Et cela fonctionne un peu: https://codesandbox.io/s/zealous-water-m8lzq?file=/src/index.js - mais gl_InstanceID n'est disponible que dans WebGL2, et il semble être cassé dans les implémentations ANGLE lorsqu'il est utilisé dans des fonctions autres que void main . Donc, ce n'est pas réaliste pour cela en ce moment.

Au lieu de cela, nous pourrions ajouter notre propre attribut d'instance, quelque chose comme attribute float charIndex; , qui ne contient qu'un index de caractères incrémentiel. Les shaders personnalisés pourraient alors en faire usage.

Je voudrais probablement en faire une fonctionnalité opt-in, quelque chose comme textmesh.includeCharIndexInShader = true , juste pour éviter de créer ce tableau d'attributs supplémentaire s'il n'est pas nécessaire.

Commentaire le plus utile

J'ai beaucoup de nettoyage à faire, mais j'ai un POC initial des comptes ci-dessus. Voici des exemples utilisant les différentes paires index/total pour changer de couleur dans un fragment shader :

carIndex / totalChars :
Screen Shot 2021-02-18 at 7 18 56 PM

carInWordIndex / totalCharsInWord :
Screen Shot 2021-02-18 at 7 19 01 PM

carInLineIndex / totalCharsInLine :
Screen Shot 2021-02-18 at 7 19 08 PM

wordIndex / totalWords : (ressemble beaucoup à charIndex/totalChars dans cet exemple mais il y a une différence subtile)
Screen Shot 2021-02-18 at 7 19 16 PM

wordInLineIndex / totalWordsInLine :
Screen Shot 2021-02-18 at 7 19 22 PM

lineIndex / totalLines :
Screen Shot 2021-02-18 at 7 19 29 PM

J'ai réussi à rassembler toutes les données dans un maximum de 3 uniformes + 2 attributs, ce qui est plutôt bien. Je veux toujours les rendre facultatifs.

Tous les 18 commentaires

Peut-être aussi : wordIndex , lineIndex ... ?

le mot et la ligne seraient activés par ligne et par animation de mot, un peu comme https://greensock.com/splittext/ (que nous pouvons considérer comme un bon exemple de ce que ce changement permettrait)

Ai-je raison de supposer que cela permettrait également des modifications par jeton (char, mot, ligne) dans le fragment shader?

Ai-je raison de supposer que cela permettrait également des modifications par jeton (char, mot, ligne) dans le fragment shader?

Vous devriez le passer du sommet au fragment en tant que variable, mais oui. :)

Dans le terrier du lapin, nous allons...

Je peux penser à des utilisations pour tous les indices suivants, plus un nombre total pour chacun :

  • carIndex, totalChars
  • motIndex, totalMots
  • lineIndex, totalLines
  • carInWordIndex, totalCharsInWord
  • charInLineIndex, totalCharsInLine
  • wordInLineIndex, totalWordsInLine

Nous voudrions rendre tous ces opt-in et faire un emballage intelligent pour minimiser le nombre de nouveaux attributs glsl que nous introduisons. Je vais devoir réfléchir davantage à l'API pour cela.

nous pourrions laisser les utilisateurs les choisir TOUS avec des drapeaux, donc

{
split : { mots : vrai, caractères : vrai, lignes : vrai }
}

faire un emballage intelligent

Ça vous dérange de développer ça ? Je suis curieux 👐

J'aime cette idée de laisser les utilisateurs choisir le(s) split(s).

Par emballage, je veux simplement dire que nous ne voulons pas ajouter 12 nouvelles déclarations attribute float foo ou nous atteindrons la limite (je pense que c'est 16 attributs au total dans webgl), mais nous pourrions les emballer dans un maximum de 3 nouvelles déclarations attribute vec4 foo , puis décompressez-les en variables float plus attrayantes dans le shader.

Oh oui, c'est très logique, merci !

Le charIndex devrait-il inclure des espaces blancs dans son incrémentation ? Ou simplement incrémenter pour les glyphes visibles ? Je penche vers des glyphes juste visibles.

Compter les espaces blancs créerait des décalages étranges lors de l'animation par index, donc je me pencherais également vers visible

J'ai beaucoup de nettoyage à faire, mais j'ai un POC initial des comptes ci-dessus. Voici des exemples utilisant les différentes paires index/total pour changer de couleur dans un fragment shader :

carIndex / totalChars :
Screen Shot 2021-02-18 at 7 18 56 PM

carInWordIndex / totalCharsInWord :
Screen Shot 2021-02-18 at 7 19 01 PM

carInLineIndex / totalCharsInLine :
Screen Shot 2021-02-18 at 7 19 08 PM

wordIndex / totalWords : (ressemble beaucoup à charIndex/totalChars dans cet exemple mais il y a une différence subtile)
Screen Shot 2021-02-18 at 7 19 16 PM

wordInLineIndex / totalWordsInLine :
Screen Shot 2021-02-18 at 7 19 22 PM

lineIndex / totalLines :
Screen Shot 2021-02-18 at 7 19 29 PM

J'ai réussi à rassembler toutes les données dans un maximum de 3 uniformes + 2 attributs, ce qui est plutôt bien. Je veux toujours les rendre facultatifs.

Étonnante! Faites-moi savoir quand vous avez une version de test prête 😄

Je cherchais à faire une fonctionnalité que cela pourrait résoudre. J'ai un tas d'étiquettes de 3 lettres à mettre autour d'une sphère. Je les fais toujours face au spectateur et j'effectue des ajustements pour qu'ils aient toujours la même taille à l'écran, quelle que soit la mise à l'échelle appliquée dans la matrice de vue ou la matrice de modélisation.
En ce moment, je crée un objet Text par étiquette et applique une transformation.
Je pensais que je pourrais obtenir de bien meilleures performances en regroupant toutes les étiquettes dans un seul texte et avoir un attribut pour avoir la position sur la sphère de chaque étiquette pour effectuer les transformations correctes dans le vertex shader.
@lojjic pouvez-vous documenter comment créer des shaders personnalisés ? Je pense que l'ajout de l'attribut ne devrait pas être difficile comme la méthode existante setAttribute de BufferGeometry devrait le faire.

L'autre option que j'envisageais serait le rendu instancié, puisque toutes mes étiquettes ont toutes 3 lettres. Mais cela nécessiterait certainement WebGL 2 et pourrait être trop complexe pour en valoir la peine, du moins au début.

@FunMiles Je pense que vous avez raison, cela peut faciliter ce genre de chose. Si je comprends bien, l'optimisation pourrait impliquer deux parties :

  1. Déplacement de la logique de rotation/mise à l'échelle du JS côté CPU vers la logique du vertex shader côté GPU
  2. Ce _plus_ combinant plusieurs morceaux de texte en un seul appel de tirage

Pour 1, jetez un oeil à ce commentaire , qui peut déjà suffire à vos besoins. C'est aussi une bonne démonstration de la façon d'appliquer un shader personnalisé, en l'attribuant simplement comme material . J'ai utilisé l'utilitaire createDerivedMaterial de Troika pour cela, mais vous pouvez également passer n'importe quel ShaderMaterial ou un matériau avec ses propres modifications onBeforeCompile .

Pour 2, je pense que vous avez raison, vous pouvez rendre un seul texte, puis utiliser l'attribut _some_ pour déplacer des caractères individuels autour de votre sphère. Les nouveaux attributs d'index de caractères/mots décrits dans ce numéro peuvent être utiles, mais je ne suis pas sûr qu'ils soient même nécessaires. Vous pourriez probablement simplement encoder ces déplacements dans un nouvel InstancedBufferAttribute, où chacun de ses vecteurs contient le déplacement pour un caractère (la géométrie du texte est un simple quad qui est instancié pour chaque glyphe, donc des InstancedBufferAttributes supplémentaires seront parcourus pour chaque glyphe.)

Je suis intéressé de voir si vous avez de la chance de faire ça !

@lojjic Vous avez bien compris. Et le code que vous avez lié fait 90% de ce dont j'ai besoin pour (1). Votre démo correspond étrangement à mon cas d'utilisation.
Si je peux vous demander de corriger ma compréhension, voici comment je vois ce que vous faites :

  • mvPosition est initialisé avec la position du point de référence dans l'espace de vue modèle. Je présume que ce point de référence est le point d'ancrage. Est-ce correct?
  • position est le décalage par rapport à ce point de référence et je suppose qu'il n'a que des coordonnées x et y non nulles. (ce qui me fait penser que vous pouvez ignorer le calcul de la composante z de l'échelle ?) (PS : en regardant le code du shader de vertex de rendu instancié, il semble que l'on puisse faire pivoter le texte, auquel cas la composante z ne sera pas nulle)
  • L'échelle dans chaque direction est récupérée en supposant que le bloc 3x3 ne contient qu'une composition d'échelle et de rotations.
  • Le position mis à l'échelle est ajouté, et si la composante z est nulle, cette contribution maintient la position dans un plan parallèle au plan de projection.

Les changements pour moi à ce code pour juste faire (1) seraient principalement dans la mise à l'échelle et un léger décalage de coordonnées afin que le texte reste en dehors de la sphère que j'ai.

Vous avez compris 2 aussi. Cependant, je suis un peu confus lors de la lecture du code. Je n'avais pas réalisé que le rendu du texte est déjà un rendu d'instance. Ce qui me déroute, c'est que vous avez GlyphsGeometry dérivant de InstancedBufferAttribute et pourtant Text est une sous-classe de Mesh et non de InstancedMesh . Je suppose que je dois creuser plus profondément dans Three.js.
Sinon, étant donné que chaque caractère est instancié, j'aurais juste besoin d'un attribut avec un vecteur pour compenser position pour chaque instance de glyphe. Est-ce correct?

J'ai dû m'éloigner de cela pendant un moment, mais voici une mise à jour rapide du statut :

Le PR # 109 semble assez solide en termes de collecte et d'exposition des différents décomptes. Je veux qu'ils s'inscrivent, mais sinon, je suis satisfait de la situation.

Cependant, j'ai une forte intuition que les animations basées sur les shaders vont nécessiter non seulement ces nouveaux décomptes, mais aussi peut-être l'accès à d'autres données comme :

  • Les limites quadruples du glyphe actuel
  • Limites globales du bloc
  • Informations métriques sur la police telles que ligne de base/ascendant/descendant qui ne peuvent pas être déduites du quad
  • Autre?

Certains d'entre eux sont déjà techniquement présents dans le shader, mais si les utilisateurs doivent en dépendre, ils devront être exposés avec des noms conviviaux et documentés comme un contrat fiable.

Si quelqu'un a le temps de jouer avec cette branche PR et d'essayer d'implémenter des animations de shader, et de me faire savoir quelles informations manquent, ce serait d'une grande aide.

@lojjic Je veux juste exprimer mon intérêt pour cette fonctionnalité 😺

J'ai résolu certains conflits de fusion avec le dernier maître ici : https://github.com/canadaduane/troika/tree/char-indices

Je n'ai pas encore eu l'occasion de le tester, mais j'ai l'intention de le faire dans les prochains jours.

J'ai été éloigné de cette discussion pendant un long moment. Cependant, on vient de me rappeler que je veux recommencer à le regarder.
À la fin de cet article se trouve un exemple de quelque chose que j'ai fait en utilisant createDerivedMaterial .
Cependant, en ce moment, il y a un maillage par étiquette et lorsque le nombre d'étiquettes devient très important, le rendu devient saccadé sur les téléphones bas de gamme. Je suppose que cela est dû aux appels CPU pour chaque étiquette. Pour résoudre ce problème, je voudrais remplacer les multiples maillages par un seul avec toute l'étiquette et utiliser un vertex shader amélioré pour faire le reste.

Je pense que, comme @lojjic l'a mentionné, j'aurais besoin de limites sur les mots. Ou du moins, pour mon usage, le centre de chaque mot.
Toute autre suggestion sur la façon de le faire autrement ou sur la façon d'obtenir ce centre?

https://flightlog-beta.vercel.app/circles?route=BRU-HER-ATH-IST-AMM-CAI-TUN-CMN-RAK-MAD-LIS-ZRH-GVA-NCE-CDG-DUS-FRA- MUC-LHR-LCY-LGW-DUB-HBA-SYD-MEL-CNS-KTM-DEL-BLR-COK-MAA-CMB-MLE-HKG-HND-NRT-KIX-ICN-GMP-MNL-DPS-JOG- CGK-SIN-KUL-PEN-LGK-BKK-SGN-HAN-LPQ-CNX-DOH-AUH-DXB-JNB-CPT-LVI-ZNZ-JRO-DAR-SSH-BUD-OTP-IPC-SCL-PMC- GRU-EZE-GIG-BSB-LIM-LPB-BOG-PTY-SJO-SJU-CUN-ACA-MEX-MTY-JFK-BOS-PHL-DCA-IAD-BWI-CLT-ATL-MSP-MIA-DFW- DTW-ORD-IAH-COS-DEN-PHX-SAN-LAX-SJC-SFO-SEA-PDX-YVR-YYZ-YUL-LGA-EWR-ASP-BNE-AKL-CHC-ZQN-VIE-SJJ-VCE- MXP-FCO-BCN-PEK-PVG-HNL-OGG-KOA-HIJ-RGN-PBH-TIP-AMS-CPH-SLC-CVG-GSP-TPE-ULN-PNH-VTE-ABQ-BUF-MCO-HKT- MDL-

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

atlmtw picture atlmtw  ·  47Commentaires

Ocelyn picture Ocelyn  ·  13Commentaires

arpu picture arpu  ·  43Commentaires

stephencorwin picture stephencorwin  ·  39Commentaires

asbjornlystrup picture asbjornlystrup  ·  7Commentaires