Troika: Recurso: adicione atributos de índice de caractere/palavra/linha à geometria do texto

Criado em 11 fev. 2021  ·  18Comentários  ·  Fonte: protectwise/troika

Shaders personalizados aplicados a instâncias de texto podem permitir alguns efeitos de animação muito bons. Para muitos deles, você deseja tratar cada caractere/glifo independentemente e, para isso, precisa de algo no sombreador informando qual caractere está sendo renderizado no momento.

Teoricamente gl_InstanceID poderia ser usado para isso, já que usamos instanciação para os quads de glifos. E isso meio que funciona: https://codesandbox.io/s/zealous-water-m8lzq?file=/src/index.js -- mas gl_InstanceID está disponível apenas no WebGL2 e parece ser quebrado em implementações ANGLE quando usado em funções diferentes de void main . Portanto, não é realisticamente utilizável para isso agora.

Em vez disso, poderíamos adicionar nosso próprio atributo de instância, algo como attribute float charIndex; , que contém apenas um índice de caractere de incremento. Shaders personalizados poderiam então fazer uso disso.

Eu provavelmente gostaria de torná-lo um recurso opcional, algo como textmesh.includeCharIndexInShader = true , apenas para evitar criar esse array de atributos extra se não for necessário.

Comentários muito úteis

Tenho muita limpeza para fazer, mas tenho um POC inicial das contagens acima. Aqui estão alguns exemplos usando os vários pares de índice/total para alterar a cor em um sombreador de fragmento:

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

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

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

wordIndex / totalWords: (parece muito semelhante a charIndex/totalChars neste exemplo, mas há uma diferença sutil)
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

Consegui colocar todos os dados em um máximo de 3 uniformes + 2 atributos, o que é muito bom. Eu ainda quero torná-los opcionais.

Todos 18 comentários

Talvez também: wordIndex , lineIndex ...?

palavra e linha seriam habilitadas por linha e por animação de palavra, bem como https://greensock.com/splittext/ (que podemos considerar um bom exemplo do que essa mudança permitiria)

Estou certo supondo que isso também permitiria alterações por token (caracter, palavra, linha) no sombreador de fragmento?

Estou certo supondo que isso também permitiria alterações por token (caracter, palavra, linha) no sombreador de fragmento?

Você teria que passar de vértice para fragmento como uma variação, mas sim. :)

Na toca do coelho nós vamos...

Posso pensar em usos para todos os seguintes índices, além de uma contagem total para cada um:

  • charIndex, totalChars
  • wordIndex, totalWords
  • lineIndex, totalLines
  • charInWordIndex, totalCharsInWord
  • charInLineIndex, totalCharsInLine
  • wordInLineIndex, totalWordsInLine

Gostaríamos de fazer todos esses opt-in e fazer algumas embalagens inteligentes para minimizar o número de novos atributos glsl que estamos introduzindo. Vou ter que pensar mais sobre a API para isso.

poderíamos deixar os usuários escolherem TODOS eles com sinalizadores, então

{
split: { palavras: verdadeiro, caracteres: verdadeiro, linhas: verdadeiro }
}

fazer alguma embalagem inteligente

Mente em expandir isso? Estou curioso 👐

Eu gosto dessa ideia de deixar os usuários escolherem a(s) divisão(ões).

Ao empacotar quero dizer apenas que não queremos adicionar 12 novas declarações attribute float foo ou atingiremos o limite (acho que são 16 atributos no total em webgl), mas poderíamos empacotá-los em no máximo 3 novos attribute vec4 foo e, em seguida, descompacte-as em variáveis float mais bonitas dentro do shader.

Ah sim, faz muito sentido, obrigado!

O charIndex deve incluir espaços em branco em seu incremento? Ou apenas incrementar para glifos visíveis? Estou inclinado para apenas glifos visíveis.

Contar espaços em branco criaria estranhas cambalhotas ao animar por índice, então eu também me inclinaria para visível

Tenho muita limpeza para fazer, mas tenho um POC inicial das contagens acima. Aqui estão alguns exemplos usando os vários pares de índice/total para alterar a cor em um sombreador de fragmento:

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

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

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

wordIndex / totalWords: (parece muito semelhante a charIndex/totalChars neste exemplo, mas há uma diferença sutil)
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

Consegui colocar todos os dados em um máximo de 3 uniformes + 2 atributos, o que é muito bom. Eu ainda quero torná-los opcionais.

Incrível! Avise-me quando tiver uma versão de teste pronta 😄

Eu estava procurando fazer um recurso que isso pudesse resolver. Eu tenho um monte de etiquetas de 3 letras para colocar em torno de uma esfera. Estou fazendo com que fiquem sempre de frente para o espectador e faço ajustes para que na tela fiquem sempre do mesmo tamanho, não importa o dimensionamento aplicado na matriz de visualização ou matriz de modelagem.
Agora, eu crio um objeto Text por rótulo e aplico uma transformação.
Eu estava pensando que poderia obter um desempenho muito melhor empacotando todos os rótulos em um único texto e ter um atributo para ter a posição na esfera de cada rótulo para fazer as transformações corretas no sombreador de vértice.
@lojjic você pode documentar como criar shaders personalizados? Acho que adicionar o atributo não deve ser difícil como o método existente setAttribute de BufferGeometry deve fazer.

A outra opção que eu estava considerando seria renderização instanciada, já que todos os meus rótulos têm 3 letras. Mas isso exigiria o WebGL 2 com certeza e pode ser muito complexo para valer a pena, pelo menos no início.

@FunMiles Acho que você está certo, isso pode facilitar esse tipo de coisa. Se estou entendendo corretamente, a otimização pode envolver duas partes:

  1. Movendo a lógica de rotação/escalonamento do JS do lado da CPU para a lógica do sombreador de vértice do lado da GPU
  2. Esse _plus_ combinando vários pedaços de texto em uma única chamada de desenho

Para 1, dê uma olhada neste comentário , que pode realmente ser suficiente para suas necessidades. Essa também é uma boa demonstração de como aplicar um sombreador personalizado, basicamente apenas atribuindo-o como material . Eu usei o utilitário createDerivedMaterial da Troika para isso, mas você também pode passar qualquer ShaderMaterial ou um material com suas próprias modificações onBeforeCompile .

Para 2, acho que você está certo, você pode renderizar um único texto e usar o atributo _some_ para deslocar caracteres individuais ao redor de sua esfera. Os novos atributos de índice de caracteres/palavras descritos nesta edição podem ser de alguma ajuda, mas não tenho certeza se são necessários. Você provavelmente poderia apenas codificar esses deslocamentos em um novo InstacedBufferAttribute, onde cada um de seus vetores contém o deslocamento para um caractere (a geometria do Text é um quad simples que é instanciado para cada glifo, portanto, InstancedBufferAttributes adicionais serão percorridos para cada glifo.)

Estou interessado em ver se você tem alguma sorte fazendo isso!

@lojjic Você entendeu corretamente. E o código que você vinculou faz 90% do que eu preciso para (1). Sua demonstração estranhamente corresponde ao meu caso de uso.
Se eu puder pedir que você corrija meu entendimento, aqui está como eu vejo o que você está fazendo:

  • mvPosition é inicializado com a posição do ponto de referência no espaço de visualização do modelo. Presumo que esse ponto de referência seja o ponto de ancoragem. Isso está certo?
  • position é o deslocamento desse ponto de referência e presumo que ele tenha apenas coordenadas x e y diferentes de zero. (o que me faz pensar que você pode pular o cálculo do componente z da escala?) (PS: Olhando para o código do sombreador de vértice de renderização instanciado, parece que é possível girar o texto, caso em que o componente z não será zero)
  • A escala em cada direção é recuperada assumindo que o bloco 3x3 contém apenas uma composição de escala e rotações.
  • O position escalado é adicionado, e se o componente z for zero, esta contribuição mantém a posição em um plano paralelo ao plano de projeção.

As mudanças para mim neste código para apenas fazer (1) seriam principalmente no dimensionamento e uma ligeira mudança de coordenada para que o texto ficasse fora da esfera que tenho.

Você entendeu 2 também. No entanto, estou um pouco confuso ao ler o código. Eu não tinha percebido que a renderização de texto já é renderização de instância. O que me confunde é que você tem GlyphsGeometry derivando de InstancedBufferAttribute e ainda assim Text é uma subclasse de Mesh e não de InstancedMesh . Acho que tenho que me aprofundar no Three.js.
Caso contrário, dado que cada caractere é instanciado, eu precisaria apenas de um atributo com um vetor para compensar position para cada instância de glifo. Isso está certo?

Eu tive que me afastar um pouco disso, mas aqui está uma rápida atualização de status:

O PR #109 parece bastante sólido em termos de coleta e exposição das várias contagens. Eu quero torná-los opt-in, mas por outro lado estou feliz com onde está.

No entanto, tenho um forte palpite de que as animações baseadas em sombreadores exigirão não apenas essas novas contagens, mas também talvez acesso a alguns outros dados, como:

  • Os limites quad do glifo atual
  • Limites gerais do bloco
  • Informações de métrica de fonte como linha de base/ascendente/descendente que não podem ser inferidas do quad
  • De outros?

Alguns deles já estão tecnicamente presentes no shader, mas se os usuários dependerem deles, eles precisarão ser expostos com nomes amigáveis ​​e documentados como um contrato confiável.

Se alguém tiver tempo para brincar com esse ramo de relações públicas e tentar implementar algumas animações de shader e me informar quais informações estão faltando, isso seria uma grande ajuda.

@lojjic Só quero expressar meu interesse neste recurso 😺

Resolvi alguns conflitos de mesclagem com o último mestre aqui: https://github.com/canadaduane/troika/tree/char-indices

Ainda não tive a oportunidade de testar, mas pretendo fazer nos próximos dias.

Estou longe dessa discussão há muito tempo. No entanto, acabei de lembrar que quero voltar a vê-lo.
No final deste post está um exemplo de algo que fiz usando createDerivedMaterial .
No entanto, agora, há uma malha por rótulo e quando o número de rótulos se torna muito grande, a renderização fica um grande espasmo em telefones de baixo custo. Estou assumindo que é devido às chamadas da CPU para cada rótulo. Para resolver isso, gostaria de substituir as várias malhas por uma única com todo o rótulo e usar um sombreador de vértice aprimorado para fazer o resto.

Eu acho que, como @lojjic mencionou, eu precisaria de alguns limites nas palavras. Ou pelo menos, para meu uso, o centro de cada palavra.
Alguma outra sugestão sobre como fazê-lo de outra forma ou como obter esse centro?

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-

Esta página foi útil?
0 / 5 - 0 avaliações