Troika: Esquema de texto

Creado en 20 ago. 2020  ·  39Comentarios  ·  Fuente: protectwise/troika

Estoy tratando de descubrir cómo tener un contorno de texto negro alrededor del texto blanco para que el texto se pueda leer fácilmente. No estoy seguro, pero parece que podría necesitar usar un sombreador para hacer con troika-tres-texto. ¿Puede proporcionar alguna orientación sobre cómo lograr este efecto?

¡Gracias!

Comentario más útil

Gracias por la información sobre Mapbox, @anzorb. Definitivamente revisaré su implementación más de cerca, pero a partir de esa publicación de blog (llaman al esquema un "halo") parece que usan el enfoque de umbral alfa SDF como mencioné antes. Pero su estrategia de generación de SDF y empaquetado de atlas es diferente a la mía y no tiene los mismos problemas con los tamaños de campo de distancia no uniformes por glifo. Sin embargo, probablemente tendría la misma limitación de ancho de contorno máximo.

De hecho, he hecho algunos progresos en los esquemas de _espacio de pantalla_ utilizando derivados estándar. Espacio de pantalla, lo que significa que el contorno tendría, por ejemplo, 1 píxel de pantalla de ancho, independientemente del tamaño/escala o la oblicuidad del texto. Si el objetivo es solo agregar contraste con el fondo, parece que eso podría ser tan bueno (o incluso preferible) como los contornos que se escalan junto con el tamaño del texto. ¿Tienes una opinión sobre eso?

Todos 39 comentarios

Casi siento que tal vez lo que necesito se logra con el indicador debugSDF, si tan solo pudiera colorearlo.
image
Mostrar el sdf básicamente me da un contorno que probablemente funcionaría si pudiera colorearlo, ajustar el tamaño y potencialmente hacerlo un poco más nítido.

Esto no debería ser demasiado difícil de hacer. Empecé a jugar con textOutline como una función hace un tiempo, pero nunca lo terminé.

En teoría, los bordes del glifo se pueden expandir simplemente cambiando el valor en el campo de distancia con signo que corresponde al borde. Eso es 0.5 en el código de sombreado actual , pero podría configurarse pasándolo como un uniforme.

Idealmente, el sombreador podría manejar dibujar tanto el glifo normal como cualquier contorno a su alrededor en una sola llamada de dibujo, por lo que necesitaría un par de uniformes nuevos: el ancho del contorno (¿no está seguro en qué unidades estaría esto?), y el color de el área del contorno.

Empecé a jugar con textOutline como una función hace un tiempo, pero nunca lo terminé.

Sería MUY feliz si fuera compatible de forma nativa :)
Sin embargo, soy muy nuevo en los sombreadores, así que no estoy seguro de cuánta ayuda puedo ser. Estaré atento a este tema.

Volví un poco a esto y rápidamente me di cuenta de por qué no lo había terminado originalmente. Si bien el SDF _puede_ usarse para delinear, hay algunas advertencias:

  • El ancho potencial máximo del contorno está limitado por la extensión del propio campo de distancia (~8 elementos de textura en la textura SDF de 64x64).
  • Eso no corresponde a un tamaño visual uniforme en todos los glifos, ya que la textura SDF se escala según los límites de cada glifo.

Así que los glifos más grandes como "W" pueden tener un contorno decentemente grueso, pero los pequeños como los puntos solo pueden tener un contorno muy delgado. Y cada glifo necesita usar un punto de corte SDF diferente para que sus contornos sean visualmente consistentes.

No creo que esto sea insuperable todavía, pero no es tan simple como pensé inicialmente.

@lojjic , ​​me encanta esta biblioteca 🥇, pero el contorno del texto es un requisito para nuestro proyecto...

Volví un poco a esto y rápidamente me di cuenta de por qué no lo había terminado originalmente. Si bien el SDF _puede_ usarse para delinear, hay algunas advertencias:

  • El ancho potencial máximo del contorno está limitado por la extensión del propio campo de distancia (~8 elementos de textura en la textura SDF de 64x64).
  • Eso no corresponde a un tamaño visual uniforme en todos los glifos, ya que la textura SDF se escala según los límites de cada glifo.

Así que los glifos más grandes como "W" pueden tener un contorno decentemente grueso, pero los pequeños como los puntos solo pueden tener un contorno muy delgado. Y cada glifo necesita usar un punto de corte SDF diferente para que sus contornos sean visualmente consistentes.

No creo que esto sea insuperable todavía, pero no es tan simple como pensé inicialmente.

Tengo curiosidad, ¿podemos hacer algún tipo de procesamiento posterior usando sombreadores para lograr un contorno y/o efectos de sombra? ¡Gracias por adelantado!
Aquí está el efecto que estoy tratando de lograr:
Screen Shot 2020-08-31 at 4 32 03 PM

Editar: esto es de mapbox gl, creo que también están usando SDF https://blog.mapbox.com/drawing-text-with-signed-distance-fields-in-mapbox-gl-b0933af6f817 , tal vez podamos diseccionar su código para ver lo que hicieron. https://github.com/mapbox/mapbox-gl-js

Gracias por la información sobre Mapbox, @anzorb. Definitivamente revisaré su implementación más de cerca, pero a partir de esa publicación de blog (llaman al esquema un "halo") parece que usan el enfoque de umbral alfa SDF como mencioné antes. Pero su estrategia de generación de SDF y empaquetado de atlas es diferente a la mía y no tiene los mismos problemas con los tamaños de campo de distancia no uniformes por glifo. Sin embargo, probablemente tendría la misma limitación de ancho de contorno máximo.

De hecho, he hecho algunos progresos en los esquemas de _espacio de pantalla_ utilizando derivados estándar. Espacio de pantalla, lo que significa que el contorno tendría, por ejemplo, 1 píxel de pantalla de ancho, independientemente del tamaño/escala o la oblicuidad del texto. Si el objetivo es solo agregar contraste con el fondo, parece que eso podría ser tan bueno (o incluso preferible) como los contornos que se escalan junto con el tamaño del texto. ¿Tienes una opinión sobre eso?

Los anchos estandarizados son mucho más preferidos que los anchos de escala. Se comportaría más como css de esta manera también.
Cualquier text-shadow que se aplique a través de css se configura independientemente del font-size .
Si su solución propuesta @lojjic funciona de la misma manera, entonces eso sería 🎉 🎉 🎉

¡Gracias por tu pronta respuesta @lojjic!

El objetivo es agregar contraste, y su propuesta se ve muy bien. ¿Quiere decir que el contorno será estático? es decir, no hay forma de controlarlo (como lo describió @stephencorwin , usando el enfoque text-shadow-ish).

Honestamente, no lo veo como un problema, siempre y cuando el 1 píxel sea visible en pantallas de súper alta densidad (porque actualmente multiplicamos el tamaño de fuente por la proporción de píxeles del dispositivo para lograr las "mismas" proporciones, independientemente de la densidad) , creo que su propuesta significa que el contorno será visiblemente más delgado en dispositivos de mayor DPI, ¿no?

¡Gracias por adelantado!

Creo que su propuesta significa que el contorno será visiblemente más delgado en dispositivos de mayor DPI, ¿no?

Sí, esa es una desventaja del enfoque de espacio de pantalla. Eso puede estar bien para muchos escenarios, pero me molestaría como diseñador tener una apariencia diferente en todos los dispositivos.

El otro gran inconveniente es que (creo) aumentar el grosor del halo tendría un impacto en el rendimiento debido a que se requieren más lecturas de textura. Aunque tengo que seguir investigando esto.

Yo también necesito esta característica. Implementé una función de este tipo en una aplicación de Android que desarrollé hace mucho tiempo y que he usado solo para mí y mis amigos. Comencé a volver a implementar esta aplicación con React y vine a buscar su proyecto.
Al igual que otros, quiero darle las gracias por esta biblioteca. Estoy asombrado por eso.

En tu comentario sobre más lecturas de texturas, no creo que debas preocuparte tanto. Los elementos de textura adicionales que necesita serán los elementos de textura que necesitan los fragmentos vecinos y, por lo tanto, creo que estarán en el caché de la GPU de todos modos y no costarán casi nada.

Gracias por el aporte @FunMiles. (¡Colorado representa! 😄)

Creo que probablemente tenga razón para contornos de 1, tal vez 2 fragmentos de espesor. Mi preocupación es que a medida que aumenta el grosor, aumenta tanto la cantidad de lecturas de textura (¿grosor de 4 = 56 lecturas?) como la probabilidad de que esas lecturas sean más caras. Pero solo estoy especulando basándome en leer algunas cosas, no soy un experto en GPU de ninguna manera. Probablemente tendré que probarlo.

@lojjic (al norte de ti aquí 😄) Traté de encontrar el código para el fragment shader. Admito que me cuesta entender las cosas, ya que parece (y creo que lo leí en alguna parte) que modificas el sombreador en lugar de escribirlo por completo.
Sin embargo, aunque no puedo descifrar cómo es el sombreador completo y cómo realmente te conectas a él, creo que entiendo algo de la rutina principal...

En este momento, lee una distancia escalar para el píxel con texture2D(uTroikaSDFTexture, vTroikaSDFTextureUV).r; . Si entiendo lo que escribiste antes, un problema tiene que ver con el espacio alrededor de los glifos. ¿Puedes explicar con más detalles?
Volviendo a su ejemplo de 'W' y ',', ¿el tramo vTroikaSDFTextureUV en la textura se ajusta exactamente alrededor del glifo, o hay algún espacio adicional? ¿El sombreador de fragmentos conoce los límites de la UV para el glifo actual? Si lo hace, para un fragmento que caería afuera, puede proyectar el UV actual a los límites e ir a leer los datos dentro.

Creo que es posible averiguar lo que se necesita con un máximo de 5 lecturas de texel o con el uso de derivados (veo que verifique si están disponibles).

Volviendo a su ejemplo de 'W' y ',', ¿el tramo vTroikaSDFTextureUV en la textura se ajusta exactamente alrededor del glifo, o hay algún espacio adicional?

Hay un pequeño espacio adicional alrededor de los límites de la ruta verdadera de cada glifo, lo suficiente para acomodar las partes exteriores del campo de distancia. Son 8 elementos de textura en el SDF, pero dado que cada SDF es un 64x64 uniforme escalado en un cuadrángulo de glifo de tamaño variable, ese margen visible varía de glifo a glifo.

¿El sombreador de fragmentos conoce los límites de la UV para el glifo actual? Si lo hace, para un fragmento que caería afuera, puede proyectar el UV actual a los límites e ir a leer los datos dentro.

Esa información debería estar disponible. Sin embargo, no te estoy siguiendo en "proyectar el UV actual hasta los límites y leer los datos dentro".

¡Estaría agradecido si conoce una manera de obtener contornos más gruesos con menos lecturas de texel! Lo he estado conceptualizando como: para un fragmento que no está dentro de la ruta del glifo, haz una búsqueda radial para ver si hay algún fragmento dentro de r=caería dentro de la ruta del glifo. Esa puede ser la forma incorrecta de pensar al respecto.

Un problema es lo que sucede cuando los glifos se juntan uno al lado del otro. Dejando eso de lado por ahora, tomemos el caso de un solo personaje dibujado con un contorno bastante grueso t . El carácter se dibuja con un rectángulo a su alrededor que es lo suficientemente grande como para acomodar el contorno.
El fragment shader tiene dos informaciones principales: vTroikaSDFTextureUV y vTroikaGlyphUV .

Tengo entendido que vTroikaGlyphUV tiene valores sujetos entre 0 y 1 cuando está dentro del glifo. Lo que quise decir cuando hablé sobre la proyección fue que si vTroikaGlyphUV está fuera de esos límites, puede recuperar la distancia del punto "proyectado" en el límite del glifo. Es decir, si uv = (1,05, 0,7), entonces el punto proyectado es (1,0, 0,7). Al usar valores en (1.0, 0.7+epsilon) y (1.0, 0.7-epsilon), puede recuperar un gradiente a la distancia y usar eso, calcular la distancia aproximada en (1.05, 0.7).
Cuando hice mi propio trabajo para esto, recuerdo haber leído un artículo de alguien que también estaba haciendo SDF con un degradado en la textura. Este enfoque evita tener que leer tres elementos de textura, pero convierte la textura en una textura de 3 componentes y agrega complejidad a la construcción de la textura.

¿Responde esto a tu pregunta?

Ahora, cuando tienes varios personajes uno al lado del otro, cada uno debe estar al tanto de los vecinos, lo que hace que esto sea un poco más complejo. Debe llevar GlyphUV y TextureUV no solo para el personaje actual, sino también para el vecino. Eso puede requerir cortar cada carácter en dos rectángulos, de modo que el vecino pueda ser el anterior para el primer rectángulo y el posterior para el segundo rectángulo. No estoy seguro de cómo lidiar con varias líneas.... No tuve que lidiar con ese caso en mi solicitud. Mi aplicación era un mapa y cada etiqueta era de una sola línea.

PD: Es posible que haya malinterpretado su "espacio adicional alrededor de los límites de la ruta verdadera de cada glifo". ¿Está diciendo que una X, por ejemplo, está encerrada en un rectángulo y que hay cuatro triángulos que no tienen valores válidos?

@FunMiles Creo que tal vez veo a dónde vas. Necesitaré algo de tiempo para pensarlo, pero tengo otro trabajo de fecha límite en el que debo concentrarme en este momento.

PD: Un poco de nota matemática divertida:
Por si alguien se pregunta cómo se puede obtener la pendiente a partir de dos o tres puntos alineados, hay que recordar que la pendiente de la distancia tiene una norma fija (dependiendo de la escala elegida). Y el degradado definitivamente apunta hacia el exterior del cuadro de glifo. Entonces, por ejemplo, si los 3 puntos del ejemplo (1.0, 0.7), (1.0, 0.7+epsilon) y (1.0, 0.7-epsilon) están a la misma distancia, entonces el gradiente es (escala, 0).

Aunque definitivamente no es eficaz, es posible usar compensaciones. Estoy usando react-three-fiber , que aprovecha troika-text con el paquete drei, pero pensé que lo compartiría en caso de que otros estén buscando una solución como un recurso provisional hasta que la biblioteca admita el trazo de forma nativa:

import React from 'react';
import {Text} from 'drei';

const StrokedText: React.FC<
  {
    strokeWidth?: number;
    strokeColor?: string;
    strokeResolution?: number;
    bold?: boolean;
  } & any
> = ({
  strokeWidth = 1,
  strokeColor: color = '#000000',
  strokeResolution = 100,
  position,
  bold,
  ...props
}) => {
  const font = bold ? FONTS.BOLD : undefined;
  const sharedProps = {
    ...props,
    font,
    color,
    sdfGlyphSize: 12,
    debugSDF: true,
  };

  let zOffset = 0;
  const offset = () => (zOffset += 0.001);

  return (
    <group name="Stroked Text" position={position}>
      {Array(strokeWidth)
        .fill({})
        .map((_, i) => {
          const s= i / strokeResolution;
          return (
            <React.Fragment key={i}>
              {/* <Text {...sharedProps} position={[-s, 0, offset()]} />*/}
              {/* <Text {...sharedProps} position={[s, 0, offset()]} />*/}
              <Text {...sharedProps} position={[0, -s, offset()]} />
              <Text {...sharedProps} position={[0, s, offset()]} />
              <Text {...sharedProps} position={[-s, -s, offset()]} />
              <Text {...sharedProps} position={[s, s, offset()]} />
              {/* <Text {...sharedProps} position={[-s, s, offset()]} /> */}
              {/* <Text {...sharedProps} position={[s, -s, offset()]} /> */}
            </React.Fragment>
          );
        })}
      <Text name="Text" {...props} position={[0, 0, strokeWidth ? offset() : 0]} />
    </group>
  );
};

export default StrokedText;

Luego, en otro lugar, puedo llamarlo con <StrokedText /> en lugar de <Text /> normalmente:

<StrokedText
  color="#ffffff"
  fontSize={fontSize}
  clipRect={[-0.5, -0.15, 0.5, 0.15]}
  textAlign="center"
  position={[0, 0, 0.01]}
>
  {text}
</StrokedText>

image

@FunMiles Finalmente tuve algo de tiempo para analizar sus sugerencias. Creo que tendría sentido suponiendo que la totalidad del rectángulo SDF del glifo contuviera valores de distancia útiles (> 0.0). Ese no es el caso actualmente; Creo que se dio cuenta en su seguimiento sobre "x", donde el SDF cae a cero dentro de los límites del quad, por lo que hay áreas significativas donde no hay un gradiente de distancia útil desde el cual extrapolar:

image

¿Quizás pueda considerar cambiar el generador SDF para garantizar un gradiente distinto de cero en la totalidad del quad ...? Pensando en voz alta, eso podría tener connotaciones de precisión de distancia y podría introducir artefactos entre personajes dentro de la textura del atlas... 🤔

Tal vez pueda considerar cambiar el generador SDF para garantizar un gradiente distinto de cero en la totalidad del quad.

Lo probé, y sí, permite que el campo de distancia se extrapole más allá de los límites cuádruples, pero como temía, reduce significativamente la calidad del glifo en sí. Eso es de esperar con solo un gradiente de 8 bits; extenderlo a una distancia mayor da como resultado una menor precisión en cada téxel. :(

Tal vez podría generar un SDF separado solo para los contornos, un campo de mayor dispersión pero de menor precisión, codificado en un segundo canal de textura. Duplicaría el tamaño de la textura pero no debería ser significativamente más lento. 🤔

codificado en un segundo canal de textura.

@lojjic Eso suena muy razonable y básicamente imita mi solución en este momento. Además, se puede optar por participar, por lo que no hay éxito de rendimiento si el usuario no solicita esquemas.

@lojjic El enfoque de participación sugerido por @stephencorwin garantizaría que nadie pague más de lo que está dispuesto a pagar.

Volvamos al aspecto técnico. Tomemos la imagen X en su respuesta. ¿Estoy entendiendo correctamente que para todos los glifos, es de 64x64 píxeles? Con 8 bits, si tiene que codificar todo el espacio de distancia, eso significa que tiene una precisión de 2 _fraccionales_ bits. Supongo que está afirmando que esta precisión no es suficiente.

Tal vez haya un truco para ganar en precisión a muy bajo costo. En lugar de hacer una cuadrícula de 64x64 de 8 bits, comprima los datos de bloques de 2x2 en datos de 32 bits. Una característica clara de la distancia con signo entre dos píxeles es que siempre está dentro de [-1,1] derecha e izquierda, arriba y abajo y [-sqrt(2), sqrt(2)] en diagonal. Así que imagina que usas 12 bits para la SD del centro del bloque 2x2, tendrías 5 bits para cada centro de los píxeles para codificar la diferencia entre su centro y el centro del 2x2. Estos 5 bits representan como máximo una distancia sqrt(2)/2. Efectivamente, tiene una precisión de 5,5 bits.
Los 12 bits del punto central codifican al menos 6 bits de precisión en una distancia máxima de 64. Técnicamente, si la caja tuviera solo un punto en una esquina, los 12 bits tendrían que poder codificar hasta sqrt(2)*64, pero No creo que eso sea realmente posible, ya que se supone que el cuadro está razonablemente centrado alrededor de cada glifo. De hecho, cuanto más lejos de cualquier borde de glifo y menos información tengan que codificar los deltas (cuando se está lejos de un borde de glifo, el campo de degradado se vuelve casi uniforme en una vecindad), por lo que desde la perspectiva de la teoría de la información, es incluso posible mejorar la codificación
para tener más bits de información real.... Pero no comenzaría con una optimización tan añadida.

Una consecuencia interesante de este enfoque es que uno no permitiría que el hardware de texturización hiciera ninguna interpolación. Pero por otro lado, uno obtendría gradientes gratis.

Si necesita ayuda, podría implementar la codificación/descodificación de todo esto.

PD: Recuerdo haber visto una discusión en uno de los README.md sobre un SDF más sofisticado. ¿Lo soñé? 😛 ¿Puede alguien señalarme para ver si ese otro sistema podría ayudar aquí?

@FunMiles Idea de compresión muy inteligente. Lo tendré en cuenta si otras opciones fallan. Perder la interpolación lineal por el hardware es una compensación que preferiría no tener que hacer. 😉

Se me ocurrió que mi problema con la falta de suficientes bits se exacerba al usar 0.5 como la distancia "cero", por lo que solo la mitad de los valores alfa están disponibles para codificar la distancia _fuera_ del glifo. Potencialmente podría cambiar eso para usar más valores para distancias exteriores y menos para distancias interiores, ganando algo de precisión en la periferia.

Re. un "SDF más sofisticado", puede querer decir "MSDF" donde se utilizan múltiples canales de color?

@FunMiles Idea de compresión muy inteligente. Lo tendré en cuenta si otras opciones fallan. Perder la interpolación lineal por el hardware es una compensación que preferiría no tener que hacer. 😉

No creo que debas preocuparte por perder esa interpolación. El costo es muy bajo, pero los beneficios de tener siempre el gradiente (e incluso un poco de curvatura) cuando no hay soporte de hardware es de mayor importancia en mi opinión. En efecto, redondea el punto de muestra y obtiene un nuevo valor de compensación para el punto de muestra redondeado. El cálculo de la cobertura parcial del fragmento para suavizado procede como lo haría normalmente con el degradado disponible.
Se me ocurrió que mi problema con la falta de suficientes bits se exacerba al usar 0.5 como la distancia "cero", por lo que solo la mitad de los valores alfa están disponibles para codificar la distancia _fuera_ del glifo. Potencialmente podría cambiar eso para usar más valores para distancias exteriores y menos para distancias interiores, ganando algo de precisión en la periferia.

Eso le dará como máximo un bit de precisión. No es para estornudar, pero aún no es tan significativo.

Re. un "SDF más sofisticado", puede querer decir "MSDF" donde se utilizan múltiples canales de color?
A eso me refería. Encontré un proyecto de GitHub al respecto en C++. ¿Tenías algo para usar eso o simplemente me confundí, combinando varias cosas que busqué hace dos semanas?

Por cierto, no creo que MSDF ayude.
¿Puedo preguntarle si podría tener un pequeño archivo .md que explique cómo compila el SDF en JavaScript? Con eso, creo que podría crear código para implementar mi idea de compresión.

El SDF está construido aquí: https://github.com/protectwise/troika/blob/master/packages/troika-three-text/src/worker/SDFGenerator.js#L134 -- no hay mucho que explicar al respecto, en su mayoría mapear texels a unidades de fuente y escribir las distancias medidas en los valores de texel. Tendríamos que agregar algunas medidas de distancia adicionales para el punto central de cada bloque de 2x2.

Tratando de envolver mi cerebro completamente alrededor de esto... Replicar la interpolación bilineal en el GLSL parece bastante simple/barato, una vez que tienes los 4 valores más cercanos. Para obtener esos valores cuando están codificados con su esquema de compresión, creo que implicará:

  • Si exactamente en el centro de un texel: 2 o 3 muestras de textura
  • Si entre los 4 texels de un bloque de 2x2: 4 muestras de textura
  • Si entre dos bloques vecinos: ~9 muestras de textura~ 7 u 8 muestras de textura
  • Si entre cuatro bloques vecinos: ~11 muestras de textura~ 13 muestras de textura

¿Estoy asimilando eso correctamente?

Oh, espera, todavía estaba pensando en una textura de un solo canal. Usando cuatro canales rgba, cada bloque de datos es solo una lectura. Entonces, como máximo 4 muestras de textura.

Empecé a leer SDFGenerator.js. Lo miraré más.

No creo que necesite más que leer un solo texto por fragmento, a menos que desee mejorar específicamente la combinación alfa para los casos de esquina en los que puede estar en un punto rodeado de bordes de glifos en todos los lados. Solo necesitas el centro más cercano del bloque 2x2 donde está el centro del fragmento.
Sin embargo, me doy cuenta de que puede haber una dificultad que no había previsto... WebGL 1.0 es muy limitado en lo que proporciona que sería útil aquí: ¡Ningún tipo de entero sin signo uint ni siquiera enteros de 32 bits! 🤯 y sin operación bit a bit... Entonces, la compresión que sugiero es un poco más complicada de escribir usando números enteros de 16 bits.

Todos estos están disponibles en WebGL 2.0, pero supongo que queremos apuntar a WebGL 1.0 aquí.

Todos estos están disponibles en WebGL 2.0, pero supongo que queremos apuntar a WebGL 1.0 aquí.

Creo que podría ser razonable restringir el esquema de texto a webgl 2 y solo detectar si el usuario tiene compatibilidad con ese navegador. Aunque, puedo entender que posiblemente se haga una versión óptima de webgl 2 con un respaldo de webgl 1. En mi opinión, podríamos comenzar con webgl 2 y dejar que se empape con la comunidad antes de intentar admitir ambos de inmediato.

Creo que tengo un enfoque alternativo para solucionar el problema de la precisión, así que @FunMiles no se preocupe por pelear con las cosas de compresión por ahora.

hola @lojjic , ​​solo revisando esto. ¿Algún progreso o cosas con las que podamos ayudar?

Nota al margen: Safari finalmente está llegando a ser compatible con WebGL2, por lo que es posible que no sea compatible con WebGL1 para esta función.

Nuestra implementación de WebGL2 está lo suficientemente bien como para habilitarla de forma predeterminada para realizar pruebas más amplias.
https://trac.webkit.org/changeset/267027/webkit

Tengo un iPhone 6 en el que nunca habrá WebGL 2.0 😒

Curiosamente, WebGL 1.0 especifica requisitos aún peores de lo que pensaba. Los enteros realmente no existen:

4.1.3 Enteros
Los números enteros se admiten principalmente como ayuda para la programación. A nivel de hardware, los números enteros reales ayudarían
implementación eficiente de bucles e índices de matriz, y unidades de textura de referencia. Sin embargo, no hay
requisito de que los números enteros en el idioma se correspondan con un tipo de número entero en el hardware. No se espera que
El hardware subyacente tiene soporte completo para una amplia gama de operaciones con enteros. Un sombreado OpenGL ES
La implementación del lenguaje puede convertir números enteros en flotantes para operar con ellos. Por lo tanto, no hay portátil
comportamiento envolvente.

Sin embargo no he perdido la esperanza. Solo tengo que repensar un poco...
Mientras tanto, @lojjic , ​​¿te importaría decirnos qué enfoque alternativo has pensado?

@FunMiles Claro. Puedo extender el campo de distancia a los bordes de la textura, mientras mantengo suficiente precisión para la forma del glifo, al codificar los valores de distancia usando una escala no lineal. Por lo tanto, las distancias muy cercanas a la ruta del glifo (dentro de 1 o 2 texels) tienen muchos valores con los que trabajar, mientras que las que están más lejos tienen menos. El sombreador solo tiene que saber cómo volver a convertir a la distancia lineal original. La forma adecuada del glifo se ve muy bien, y la menor precisión de los contornos extruidos apenas se nota, ya que se redondean de todos modos.

He probado esto usando una escala lineal de dos niveles y una escala exponencial. Ambos tienen pros y contras.

Ahora estoy en medio de la implementación de su enfoque (muy inteligente) de usar valores de borde vecinos para aproximar el gradiente fuera del cuadrante. Tiene sentido hasta ahora, aunque no estoy seguro de qué hacer con las áreas fuera de las esquinas. Puede volverse obvio una vez que profundice en ello, pero si tiene una respuesta fácil para mí, se lo agradecería :)

Ahora estoy en medio de la implementación de su enfoque (muy inteligente) de usar valores de borde vecinos para aproximar el gradiente fuera del cuadrante. Tiene sentido hasta ahora, aunque no estoy seguro de qué hacer con las áreas fuera de las esquinas. Puede volverse obvio una vez que profundice en ello, pero si tiene una respuesta fácil para mí, se lo agradecería :)

No estoy seguro de lo que quieres decir con _fuera de las esquinas_. ¿Te refieres a los cuartos de plano enraizados en cada esquina, donde la proyección más cercana es la esquina misma? Creo que allí puede usar la regla en un borde tanto en el borde vertical como en el horizontal y hacer un promedio ponderado basado en la distancia a cada uno.

PD: La idea de reducir la precisión a lo lejos se me había ocurrido hoy después de leer las tonterías de los enteros de WebGL... 😛 Todavía tengo la esperanza de que la técnica de compresión se pueda implementar en WebGL 1.0 para obtener 11 bits de precisión a bajo costo y unos pocos más bits con más pruebas. es decir, usando un rgba , el a podría contener 8 bits del promedio, luego para cada rgb , se firmarían y el signo representaría 1 bit cada uno de precisión para el promedio, luego finalmente el resto, que estaría en el rango 0-127 tendría el valor 64 restado para representar 7 bits para 3 de los 4 valores necesarios. El valor real sería el centro (que oscila entre 0 y 2^11-1) + dist_i. El cuarto valor se recuperaría restando su suma, ya que la suma debe ser el promedio. Uno solo obtendría 11 bits, pero eso probablemente sea suficiente. Para obtener más bits sería necesario probar si el valor o r , g y b son menores que -64, positivos o negativos, mayores que 64. Eso esencialmente dé 14 bits para el centro y solo 6 bits para las diferencias. ¿Probablemente un buen compromiso? Necesitaría probar que las garantías muy laxas que WebGL 1.0 pone en precisión no interferirían con este pensamiento...

Sí, eso es lo que quise decir, las áreas donde tanto U como V están fuera de 0-1. Gracias, le daré una oportunidad.

@lojjic Como pregunta aparte, en una publicación anterior, parecía preocupado por tener dos valores de textura por texel SDF debido a la memoria. Aunque yo mismo prefiero reducir la memoria, 256 glifos en una cuadrícula de 64x64 solo consumen 1/4 MB de memoria de textura. Sé que algunos idiomas pueden requerir muchos más glifos, pero aún así, la BBC dice que para leer el periódico, solo se necesitan 2000/3000 caracteres. Entonces, 4000 caracteres harían 4 MB con un byte por texel SDF. ¿Vale la pena preocuparse?

@FunMiles Punto justo, y en realidad no estaba muy preocupado por eso.

Queda un trabajo pendiente por hacer, pero b19cd3aff4b0876253d76b3fc66b7e2a1f16a7e5 soluciona este problema. Para aquellos que usan react-three-fiber, esto se ha lanzado en drei v2.2.0
https://github.com/pmndrs/drei/pull/156

Sé que esto está cerrado, pero quiero dar las gracias por el trabajo. Conseguí que funcionara en mi aplicación. Tomó un poco de tiempo darse cuenta de que las propiedades no aceptarían solo un número para el tamaño del contorno. Sin embargo, el resultado es justo lo que necesitaba.
Screen Shot 2020-11-09 at 7 44 52 PM

@FunMiles Se ve bien! _Debería_ poder usar un valor numérico para outlineWidth , así que si eso no funciona para usted por alguna razón, hágamelo saber.

Y gracias por su aporte en el camino. Todavía no pude obtener una extrapolación fluida de la distancia fuera de los límites cuádruples usando el tipo de técnica que mencionaste, por lo que un contorno grueso actualmente tiene partes grumosas en las esquinas en particular, pero es lo suficientemente bueno para la mayoría de los casos (hasta ~ 10% del tamaño de la letra). Definitivamente estoy abierto a tratar de refinar eso todavía.

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