Troika: Idea rápida para mejorar el antialiasing de texto

Creado en 17 jun. 2021  ·  7Comentarios  ·  Fuente: protectwise/troika

No estoy seguro de si ha actualizado el código de suavizado recientemente, ya que estoy usando una versión anterior, pero después de encontrar una buena manera de suavizar los círculos dibujados por sombreadores, también quería probarlo en su texto. , porque el texto está basado en SDF y el antialiasing del círculo también está basado en la distancia.

En mi versión actual de Troika, haces return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.5; por la distancia aa. Esto tiene el artefacto de que cuando se ve en ángulo, los bordes más a la izquierda y más a la derecha se ven borrosos algo visibles:
image

Lo cambié a un escalar; la distancia, al igual que en mi código de círculo: return fwidth(distance) * 0.5; Con la distancia pasada desde troikaGetFragDistValue así: float distance = troikaGetFragDistValue(); float aaDist = troikaGetAADist(distance);
Al hacerlo de esta manera, no obtengo el artefacto borroso:
image

El factor 0.5 se puede aumentar para un antialiasing más suave. Por ejemplo 0.8 :
image

Pensé que tal vez no conocías este método como yo, así que pensé en compartirlo. Sin embargo, mi "implementación" tiene algunos artefactos extraños (ver más abajo), y tal vez es por eso que optó por el otro método. Además, no estoy exactamente seguro de cómo usa la variable aaDist en el código de sombreado, por lo que podría estar mejor equipado para implementar esto. Tal vez haya algún código que deba cambiarse o eliminarse debido a este cambio, por ejemplo, que podría mejorar aún más mi "implementación".

image

Encontré este método para hacerlo en la publicación de bgolus aquí: https://forum.unity.com/threads/antialiasing-circle-shader.432119/ donde hay más información. También dice que puede hacer, por ejemplo length(vec2(dFdx(distance), dFdy(distance))) en lugar de fwidth(distance) para una precisión aún mayor, aunque no estoy seguro de si esto es insignificante o no.

Todos 7 comentarios

¡Gracias por la sugerencia! Definitivamente investigaré esto, ya que también noté esos bordes borrosos en ángulos oblicuos y me gustaría eliminarlos.

En teoría, creo que tanto el cambio en distance como el cambio en vTroikaGlyphUV * vTroikaGlyphDimensions deberían ser equivalentes, ya que ambos representan una distancia en unidades de espacio de fuente, pero debe haber alguna diferencia sutil. pasado por alto. Si puedo evitar los artefactos que mostraste, estaré feliz de integrar tu cambio. :)

¡Genial! El cambio es tan simple como esa línea (y pasar ese argumento de distancia para acomodarlo). Si aún desea el código sin formato, puedo enviárselo mañana cuando esté trabajando.

En teoría, creo que tanto el cambio en la distancia como el cambio en vTroikaGlyphUV * vTroikaGlyphDimensions deberían ser equivalentes, ya que ambos representan una distancia en unidades de espacio de fuente

No entiendo exactamente cómo los derivados serían los mismos. Supongo que distance es la distancia SDF, pero la otra es una posición uv escalada. No he mirado el código y cómo se comparan, pero parece que sus derivados deberían ser diferentes. Por lo que sé fwidth solo toma la diferencia del valor de entrada específico pasado como argumento con esos mismos valores de entrada específicos en los fragmentos vecinos, lo cual es posible porque los fragmentos vecinos se ejecutan al mismo tiempo en paralelo. Realicé una prueba hoy con gl_FragColor.rgb = vec3(fwidth(gl_FragColor.x + gl_FragColor.g + gl_FragColor.b)); para ver si generaba bordes como en la detección de bordes/un filtro sobel, para intentar confirmar un poco ese hecho, y lo hizo.

Gracias por la discusión, estas cosas me rompen el cerebro a veces. ;)

Cuando dije que son "equivalentes" quise decir que ambos representan una distancia en las mismas unidades, pero tienes razón, no son exactamente iguales.

Retrocediendo, lo que queremos aquí es la tasa de cambio _potencial_ entre fragmentos. Usar el cambio en la variable distance como sugiere a menudo será preciso para eso, pero en algunos casos será muy inexacto. Esto se debe a que el SDF del que se muestrea ese valor es _no uniforme_; se curva y cambia su dirección de cambio a lo largo del glifo. Por lo tanto, la derivada de distance entre fragmentos vecinos puede terminar siendo demasiado pequeña, incluso cero, como cuando el campo de distancia cambia de creciente a decreciente a mitad de camino entre las rutas de los glifos, o demasiado grande, como cuando se muestra el texto. en tamaños pequeños y la cuadrícula de fragmentos se superpone al SDF en ubicaciones impredecibles. Sospecho que eso explica al menos algunos de los artefactos que estás viendo.

Entonces, ¿cómo podemos determinar la tasa de cambio _potencial_ sin usar distance ? Bueno, esa "posición uv escalada", como la llamas, es esencialmente una coordenada x/y dentro del rect del glifo, usando las mismas unidades que distance . La derivada de x/y nos da una tasa de viaje potencial entre fragmentos. En general, da un mejor resultado que usar distance , porque se basa en una tasa de cambio _uniforme_ en todo el glifo y, por lo tanto, no obtenemos artefactos y es más preciso en tamaños pequeños.

Pero, como has visto, donde se rompe es en los ángulos oblicuos; en esos casos, la tasa de cambio potencial x/y en la vertical es muy diferente a la de la horizontal. Y así terminamos usando un solo valor para la tasa de cambio potencial que es demasiado grande en una dirección y demasiado pequeña en la otra, dando líneas borrosas y/o entrecortadas dependiendo de la dirección de cambio real del SDF en ese fragmento.

Así que sí, definitivamente hay espacio para mejorar en el caso oblicuo, pero solo usar la derivada de distance no es suficiente por sí solo. Estoy abierto a ideas para mejorar esto, por supuesto. Una posibilidad podría ser tratar de detectar el escenario (tamaño de texto grande en un ángulo oblicuo, cerca de una ruta) y usar la derivada distance solo en ese caso.

Sí, es confuso pensar en estas cosas, jaja. Puedo ver que el campo de distancia es menos suave, sí.

Esos píxeles defectuosos podrían deberse a que los fragmentos tienen algunos valores de distancia extraños.

No es increíblemente agitado desde lejos, pero definitivamente puedes ver que los rayos UV son mejores. Quizás interpolar entre la variable de distancia y los UV que dependen de la distancia de la cámara funcionaría bien; como en elegir más ancho (distancia) cuando está cerca, y más ancho (uv) cuando está lejos.

A modo de comparación (no capturé los dos exactamente en la misma posición y orientación de la cámara, pero creo que muestra bien la diferencia de todos modos):
Distancia (factor 0.8):
image
UV:
image

Me doy cuenta de que esta modificación mejora significativamente el resultado de las transformaciones de texto personalizadas. Aquí hay algo de texto, modificado para seguir una curva usando createDerivedMaterial ( vea esta técnica ). Esto utiliza sombreadores troika-tres-texto actuales:

image

Aquí está la misma captura de pantalla, pero con la modificación de @asbjornlystrup a troikaGetAADist :

image

En la segunda toma, se puede ver el "¡GENIAL!" extremadamente estirado. es bastante más crujiente.

Como anécdota: también descubrí que ajustar la constante a 0.25 (desde 0.5) también produce un buen borde nítido:

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

Gracias @canadaduane , definitivamente puedo ver la mejora en las partes estiradas. También puedo ver los feos artefactos que aparecen entre las L en HOLA, que definitivamente tendríamos que resolver antes de adoptar este cambio.

¿Fue útil esta página
0 / 5 - 0 calificaciones