Troika: Ideia rápida para melhorar a suavização de texto

Criado em 17 jun. 2021  ·  7Comentários  ·  Fonte: protectwise/troika

Não tenho certeza se você atualizou o código de antialiasing recentemente, pois estou usando uma versão mais antiga, mas depois de encontrar uma boa maneira de fazer antialiasing de círculos desenhados por sombreamento, queria experimentá-lo em seu texto também , porque o texto é baseado em SDF e o antialiasing do círculo também é baseado em distância.

Na minha versão atual da Troika, você faz return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.5; para a distância aa. Isso tem o artefato de que, ao visualizar em ângulo, as bordas mais à esquerda e à direita ficam um pouco visíveis desfocadas:
image

Eu mudei para um escalar; a distância, assim como no meu código de círculo: return fwidth(distance) * 0.5; Com a distância passada de troikaGetFragDistValue assim: float distance = troikaGetFragDistValue(); float aaDist = troikaGetAADist(distance);
Fazendo assim, não recebo o artefato de desfoque:
image

O fator 0.5 pode ser aumentado para um antialiasing mais suave. Ex 0.8 :
image

Eu pensei que talvez você não conhecesse esse método assim como eu, então pensei em compartilhá-lo. Minha "implementação" tem alguns artefatos estranhos (veja abaixo), e talvez seja por isso que você optou pelo outro método. Além disso, não sei exatamente como você usa a variável aaDist no código do sombreador, portanto, você pode estar melhor equipado para implementar isso. Talvez haja algum código que deva ser alterado ou removido por causa dessa alteração, por exemplo, que poderia melhorar ainda mais minha "implementação".

image

Eu encontrei este método de fazer isso no post do bgolus aqui: https://forum.unity.com/threads/antialiasing-circle-shader.432119/ onde há mais informações. Ele também diz que você pode fazer, por exemplo length(vec2(dFdx(distance), dFdy(distance))) em vez de fwidth(distance) para uma precisão ainda maior, embora eu não tenha certeza se isso é insignificante ou não.

Todos 7 comentários

Obrigado pela sugestão! Definitivamente vou olhar para isso, pois notei essas bordas borradas em ângulos oblíquos também e gostaria de eliminá-las.

Teoricamente, acho que tanto a mudança em distance quanto a mudança em vTroikaGlyphUV * vTroikaGlyphDimensions devem ser equivalentes, pois ambas representam uma distância em unidades de espaço de fonte, mas deve haver alguma diferença sutil lá. esquecido. Se eu puder evitar os artefatos que você mostrou, ficarei feliz em integrar sua mudança. :)

Excelente! A mudança é tão simples quanto essa linha (e passando esse argumento de distância para acomodá-lo). Se você ainda quiser o código bruto, posso enviá-lo quando estiver trabalhando amanhã.

Teoricamente eu acho que tanto a mudança na distância quanto a mudança no vTroikaGlyphUV * vTroikaGlyphDimensions devem ser equivalentes, pois ambos representam uma distância em unidades de espaço de fonte

Eu não entendo exatamente como as derivadas seriam as mesmas. distance é a distância SDF, eu acho, mas a outra é uma posição uv dimensionada. Eu não olhei para o código e como eles se comparam, mas parece que seus derivados devem ser diferentes. Até onde eu sei fwidth apenas leva a diferença do valor de entrada específico passado como argumento com esses mesmos valores de entrada específicos nos fragmentos vizinhos, o que é possível porque os fragmentos vizinhos são executados ao mesmo tempo em paralelo. Fiz um teste hoje cedo com gl_FragColor.rgb = vec3(fwidth(gl_FragColor.x + gl_FragColor.g + gl_FragColor.b)); para ver se renderizava bordas como na detecção de bordas/um filtro sobel, para tentar confirmar um pouco esse fato, e funcionou.

Obrigado pela discussão, essas coisas quebram meu cérebro às vezes. ;)

Quando eu disse que eles são "equivalentes", eu quis dizer que ambos representam uma distância nas mesmas unidades, mas você está certo, eles não são exatamente iguais.

Fazendo backup, o que queremos aqui é a taxa _potencial_ de mudança entre fragmentos. Usar a alteração na variável distance como você está sugerindo geralmente será preciso para isso, mas em alguns casos será altamente impreciso. Isso ocorre porque o SDF do qual esse valor é amostrado é _não uniforme_; ele se curva e muda sua direção de mudança no glifo. Portanto, a derivada de distance entre fragmentos vizinhos pode acabar sendo muito pequena, até mesmo zero, como quando o campo de distância muda de crescente para decrescente na metade dos caminhos de glifo, ou muito grande, como quando o texto é exibido em tamanhos pequenos e a grade de fragmentos sobrepõe o SDF em locais imprevisíveis. Suspeito que isso explique pelo menos alguns dos artefatos que você está vendo.

Então, como podemos determinar a taxa de variação _potencial_ sem usar distance ? Bem, essa "posição uv escalada", como você chama, é essencialmente uma coordenada x/y dentro da reta do glifo, usando as mesmas unidades que distance . A derivada desse x/y nos dá uma taxa de viagem potencial entre os fragmentos. No geral, isso dá um resultado melhor do que usar distance , porque é baseado em uma taxa de alteração _uniforme_ no glifo e, portanto, não obtemos artefatos e é mais preciso em tamanhos pequenos.

Mas, como você viu, onde ele quebra é em ângulos oblíquos; nesses casos, a taxa de variação potencial x/y na vertical é muito diferente da da horizontal. E assim acabamos usando um único valor para a taxa potencial de mudança que é muito grande em uma direção e muito pequena na outra, dando linhas borradas e/ou irregulares dependendo da direção real de mudança do SDF naquele fragmento.

Então, sim, definitivamente há espaço para melhorias no caso oblíquo, mas apenas usar a derivada de distance não é suficiente por si só. Estou aberto a ideias para melhorar isso, é claro. Uma possibilidade pode ser tentar detectar o cenário (tamanho de texto grande em um ângulo oblíquo, próximo a um caminho) e usar a derivada distance apenas nesse caso.

Sim, essas coisas são confusas de se pensar, haha. Eu posso ver o campo de distância sendo menos suave, sim.

Esses pixels com falhas podem ser apenas porque os fragmentos por aí têm alguns valores de distância estranhos.

Não é incrivelmente instável de longe, mas você pode definitivamente ver os UVs sendo melhores. Talvez a interpolação entre a variável de distância e os UVs dependentes da distância da câmera funcione bem; como em escolher mais largura (distância) quando perto, e mais largura (uv) quando longe.

Para comparação (não capturou os dois exatamente na mesma posição e orientação da câmera, mas acho que mostra bem a diferença de qualquer maneira):
Distância (fator 0,8):
image
UV:
image

Estou percebendo que essa modificação melhora significativamente o resultado para transformações de texto personalizadas. Aqui está algum texto, modificado para seguir uma curva usando createDerivedMaterial ( veja esta técnica ). Isso usa os shaders de texto de três troikas atuais:

image

Aqui está a mesma captura de tela, mas com a modificação de @asbjornlystrup para troikaGetAADist :

image

Na segunda foto, você pode ver o extremamente esticado "GREAT!" é um pouco mais crocante.

Curiosamente: também descobri que ajustar a constante para 0,25 (de 0,5) também cria uma boa borda nítida:

return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.25;

Obrigado @canadaduane , posso definitivamente ver a melhora nas partes esticadas. Também posso ver os artefatos feios surgindo entre os L's em HELLO, que definitivamente precisaríamos descobrir antes de adotar essa mudança.

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

Questões relacionadas

lojjic picture lojjic  ·  18Comentários

arpu picture arpu  ·  43Comentários

stephencorwin picture stephencorwin  ·  39Comentários

atlmtw picture atlmtw  ·  47Comentários

Ocelyn picture Ocelyn  ·  13Comentários