¿Cuál es el estado del arte para la representación de texto en OpenGL a partir de la versión 4.1?
freetype 2.4 3 (5)
Ya hay una serie de preguntas sobre la representación de texto en OpenGL, como por ejemplo:
Pero principalmente lo que se discute es renderizar cuadrículas con textura usando la tubería de función fija. Sin duda, los sombreadores deben hacer una mejor manera.
No estoy realmente preocupado por la internacionalización, la mayoría de mis cadenas serán etiquetas de trama (fecha y hora o puramente numéricas). Pero las tramas se volverán a renderizar a la velocidad de actualización de la pantalla y podría haber bastante texto (no más de unos pocos glifos en pantalla, pero suficiente como para que el diseño acelerado por hardware sea agradable).
¿Cuál es el enfoque recomendado para la representación de texto con OpenGL moderno? (Citar software existente utilizando el enfoque es una buena evidencia de que funciona bien)
- Sombreadores de geometría que aceptan, por ejemplo, posición y orientación y una secuencia de caracteres y emiten cuadrículas con textura
- Sombreadores de geometría que procesan fuentes vectoriales
- Como se indicó anteriormente, pero utilizando sombreadores de teselación en su lugar
- Un sombreador de cómputo para hacer rasterización de fuentes
Creo que tu mejor opción sería mirar los gráficos de cairo con OpenGL back-end.
El único problema que tuve al desarrollar un prototipo con 3.3 núcleos fue el uso de la función en desuso en el back-end de OpenGL. Fue hace 1-2 años, así que la situación podría haber mejorado ...
De todos modos, espero que en el futuro los controladores de gráficos OpenLight implementen OpenVG.
La representación de contornos, a menos que represente solo una docena de caracteres, sigue siendo un "no ir" debido a la cantidad de vértices necesarios por carácter para aproximar la curvatura. Aunque ha habido enfoques para evaluar las curvas de bezier en el sombreador de píxeles, estos no se antialiasan fácilmente, lo cual es trivial usando un cuadrante con textura de mapa de distancia, y la evaluación de las curvas en el sombreador sigue siendo mucho más costosa de lo necesario.
La mejor solución de compromiso entre "rápido" y "calidad" sigue siendo cuadrícula texturizada con una textura de campo de distancia con signo. Es un poco más lento que el uso de un quad con textura normal, pero no tanto. La calidad, por otro lado, se encuentra en un estadio completamente diferente. Los resultados son realmente impresionantes, es lo más rápido que se puede obtener, y los efectos como el resplandor también son fáciles de agregar. Además, la técnica se puede degradar muy bien a hardware antiguo, si es necesario.
Vea el famoso papel Valve para la técnica.
La técnica es conceptualmente similar a cómo funcionan las superficies implícitas (metaballs y demás), aunque no genera polígonos. Se ejecuta completamente en el sombreador de píxeles y toma la distancia muestreada de la textura como una función de distancia. Todo lo que está por encima de un umbral elegido (generalmente 0.5) está "en", todo lo demás está "fuera". En el caso más simple, en un hardware que no tiene shader de 10 años, establecer el umbral de prueba alfa en 0.5 hará exactamente eso (aunque sin efectos especiales y antialiasing).
Si uno quiere agregar un poco más de peso a la fuente (negrita falsa), un umbral un poco más pequeño hará el truco sin modificar una sola línea de código (simplemente cambie su uniforme "font_weight"). Para un efecto de resplandor, uno simplemente considera que todo lo que está por encima de un umbral está "en" y todo por encima de otro (más pequeño) como "fuera, pero en resplandor" y LERP entre los dos. Antialiasing funciona de manera similar.
Al usar un valor de distancia de 8 bits en lugar de un solo bit, esta técnica aumenta 16 veces la resolución efectiva de su mapa de texturas en cada dimensión (en lugar de blanco y negro, se usan todos los tonos posibles, así tenemos 256 veces el información usando el mismo almacenamiento). Pero incluso si aumenta mucho más allá de 16x, el resultado aún parece bastante aceptable. Las líneas rectas largas eventualmente se volverán un poco onduladas, pero no habrá artefactos típicos de muestreo "en bloques".
Puede usar un sombreador de geometría para generar los cuadrantes de los puntos (reducir el ancho de banda del bus), pero honestamente las ganancias son bastante marginales. Lo mismo es cierto para la representación de personajes instanciados como se describe en GPG8. La sobrecarga de instancias solo se amortiza si tiene mucho texto para dibujar. Las ganancias son, en mi opinión, en ninguna relación con la complejidad añadida y la no posibilidad de volver a la baja. Además, está limitado por la cantidad de registros constantes, o tiene que leer desde un objeto de búfer de textura, que no es óptimo para la coherencia del caché (¡y la intención era optimizarlo para empezar!).
Un búfer de vértices simple, simple y viejo es igual de rápido (posiblemente más rápido) si programa la carga un poco más adelante en el tiempo y se ejecutará en todos los hardware construidos durante los últimos 15 años. Y, no se limita a ningún número particular de caracteres en su fuente, ni a un número particular de caracteres para representar.
Si está seguro de que no tiene más de 256 caracteres en su fuente, puede valer la pena considerar las matrices de texturas para quitar el ancho de banda del bus de una manera similar a la generación de quads a partir de puntos en el sombreador de geometría. Cuando se usa una textura de matriz, las coordenadas de textura de todos los cuadrantes tienen coordenadas s
t
idénticas y constantes, y solo difieren en la coordenada r
, que es igual al índice de caracteres para representar.
Pero al igual que con las otras técnicas, las ganancias esperadas son marginales a costa de ser incompatibles con el hardware de la generación anterior.
Hay una herramienta útil de Jonathan Dummer para generar texturas a distancia: página de descripción
Actualizar:
Como se señaló más recientemente en Tiraje de vértices programable (D. Rákos, "OpenGL Insights", pág. 239), no hay una latencia adicional significativa asociada con extraer datos de vértices mediante programación del sombreador en las generaciones más nuevas de GPU, en comparación con hacer lo mismo usando la función fija estándar.
Además, las últimas generaciones de GPU tienen cada vez más cachés L2 de propósito general de tamaño razonable (por ejemplo, 1536kiB en nvidia Kepler), por lo que uno puede esperar el problema de acceso incoherente cuando se extraen desplazamientos aleatorios para las esquinas cuádruples. problema.
Esto hace que la idea de extraer datos constantes (como los tamaños de cuadrantes) de una textura de búfer sea más atractiva. Una implementación hipotética podría reducir las transferencias PCIe y de memoria, así como la memoria GPU, a un mínimo con un enfoque como este:
- Cargue únicamente un índice de caracteres (uno por carácter para mostrar) como la única entrada a un sombreador de vértices que transmite este índice y
gl_VertexID
, ygl_VertexID
a 4 puntos en el sombreador de geometría, teniendo aún el índice de caracteres y el ID de vértice ( esto será "gl_primitiveID puesto a disposición en el sombreador de vértices") como únicos atributos, y captura esto a través de la realimentación de transformación. - Esto será rápido, porque solo hay dos atributos de salida (cuello de botella principal en GS), y está cerca de "no operación" de lo contrario en ambas etapas.
- Enlaza una textura de búfer que contiene, para cada carácter de la fuente, las posiciones de los vértices del cuadrante texturizado en relación con el punto base (estas son básicamente las "métricas de fuente"). Estos datos se pueden comprimir a 4 números por cuadrante almacenando solo el desplazamiento del vértice inferior izquierdo, y codificando el ancho y la altura del recuadro alineado con el eje (suponiendo medios flotantes, esto será 8 bytes de memoria intermedia constante por carácter - una fuente típica de 256 caracteres podría caber completamente en 2kiB de caché L1).
- Establecer un uniforme para la línea de base
- Enlaza una textura de búfer con desplazamientos horizontales. Probablemente podrían calcularse incluso en la GPU, pero es mucho más fácil y más eficiente para ese tipo de cosas en la CPU, ya que es una operación estrictamente secuencial y nada trivial (piense en kerning). Además, necesitaría otro pase de retroalimentación, que sería otro punto de sincronización.
- Renderice los datos generados previamente del búfer de realimentación, el sombreador de vértices extrae el desplazamiento horizontal del punto base y los desplazamientos de los vértices de esquina de los objetos de búfer (utilizando el ID primitivo y el índice de caracteres). La ID del vértice original de los vértices enviados es ahora nuestra "ID primitiva" (recuerde que GS convirtió los vértices en quads).
De esta manera, idealmente se podría reducir el ancho de banda del vértice requerido en un 75% (amortizado), aunque solo sería capaz de representar una sola línea. Si uno quisiera poder representar varias líneas en una llamada de sorteo, se necesitaría agregar la línea base a la textura del búfer, en lugar de usar un uniforme (haciendo que las ganancias de ancho de banda sean más pequeñas).
Sin embargo, incluso suponiendo una reducción del 75%, dado que los datos de vértice para mostrar cantidades "razonables" de texto están solo en algún lugar alrededor de 50-100kiB (que es prácticamente cero para una GPU o un bus PCIe) - todavía dudo que el la complejidad y la pérdida de compatibilidad hacia atrás realmente valen la pena. Reducir el cero en un 75% sigue siendo solo cero. Es cierto que no he probado el enfoque anterior, y se necesitaría más investigación para hacer una declaración verdaderamente calificada. Pero aún así, a menos que alguien pueda demostrar una diferencia de rendimiento verdaderamente deslumbrante (usando cantidades de texto "normales", ¡no miles de millones de caracteres!), Mi punto de vista sigue siendo que para los datos de vértice, un búfer de vértices simple, simple y viejo es justificadamente lo suficientemente bueno ser considerado parte de una "solución de vanguardia". Es simple y directo, funciona y funciona bien.
Tras haber mencionado anteriormente " OpenGL Insights ", también vale la pena señalar el capítulo "Representación de forma 2D por campos de distancia" de Stefan Gustavson, que explica la representación del campo a distancia con gran detalle.
Actualización 2016:
Mientras tanto, existen varias técnicas adicionales que apuntan a eliminar los artefactos de redondeo de esquina que se vuelven molestos a ampliaciones extremas.
Un enfoque simplemente usa campos de pseudo-distancia en lugar de campos de distancia (la diferencia es que la distancia es la distancia más corta no al contorno real, sino al contorno o una línea imaginaria que sobresale del borde). Esto es algo mejor, y se ejecuta a la misma velocidad (sombreador idéntico), utilizando la misma cantidad de memoria de textura.
Otro enfoque utiliza la mediana de tres en los detalles de textura de tres canales y la implementación disponible en github . Esto tiene como objetivo ser una mejora con respecto a los piratas informáticos o piratas informáticos utilizados anteriormente para abordar el problema. Buena calidad, levemente, casi no perceptiblemente, más lenta, pero usa tres veces más memoria de textura. Además, los efectos adicionales (por ejemplo, brillo) son más difíciles de resolver.
Por último, almacenar las curvas de bezier reales que componen los personajes, y evaluarlos en un sombreador de fragmentos se ha vuelto práctico , con un rendimiento ligeramente inferior (pero no tanto como para ser un problema) y resultados sorprendentes incluso con los mayores aumentos.
La demo WebGL representa un PDF grande con esta técnica en tiempo real disponible here .
La técnica más extendida sigue siendo los cuádriceps con textura. Sin embargo, en 2005 LORIA desarrolló algo llamado texturas vectoriales, es decir, representando gráficos vectoriales como texturas en primitivas. Si uno usa esto para convertir fuentes TrueType u OpenType en una textura vectorial, obtienes esto:
http://alice.loria.fr/index.php/publications.html?Paper=VTM@2005
Me sorprende que el bebé de Mark Kilgard, NV_path_rendering (NVpr), no haya sido mencionado por ninguno de los anteriores. Aunque sus objetivos son más generales que la representación de fuentes, también puede representar texto a partir de fuentes y con kerning. Ni siquiera requiere OpenGL 4.1, pero es una extensión de solo proveedor / Nvidia en este momento. Básicamente transforma las fuentes en rutas usando glPathGlyphsNV
que depende de la biblioteca freetype2 para obtener las métricas, etc. Luego también puede acceder a la información de interletraje con glGetPathSpacingNV
y usar el mecanismo de representación de rutas generales de NVpr para mostrar el texto usando las fuentes de ruta "convertidas" . (Pongo entre comillas, porque no hay una conversión real, las curvas se usan como están).
La demostración grabada para las capacidades de fuente de NVpr lamentablemente no es particularmente impresionante. (Tal vez alguien debería hacer uno a lo largo de las líneas de la demo SDF mucho más elegante que uno puede encontrar en los intertubos ...)
La charla de presentación de la API de NVpr 2011 para la parte de fuentes comienza aquí y continúa en la siguiente parte ; es un poco desafortunado cómo se divide esa presentación.
Materiales más generales sobre NVpr:
- Hub NVvidia NVpr , pero parte del material en la página de destino no es el más actualizado
- Documento de Siggraph 2012 para los cerebros del método de representación de ruta, llamado "galería de símbolos, luego cubierta" (StC); el documento también explica brevemente cómo funciona la tecnología competitiva como Direct2D. Los bits relacionados con la fuente se han relegado a un anexo del documento . También hay algunos extras como videos / demos .
- Presentación de GTC 2014 para un estado de actualización; en pocas palabras: ahora es compatible con Google Skia (Nvidia contribuyó con el código a finales de 2013 y 2014), que a su vez se usa en Google Chrome y [independientemente de Skia, creo] en una versión beta de Adobe Illustrator CC 2014
- la documentación oficial en el registro de extensiones OpenGL
- USPTO ha otorgado al menos cuatro patentes a Kilgard / Nvidia en relación con NVpr, de las que probablemente debas estar al tanto, en caso de que desees implementar StC por ti mismo: US8698837 , US8698808 , US8704830 y US8730253 . Tenga en cuenta que hay algo así como otros 17 documentos de la USPTO conectados a esto como "también publicados como", la mayoría de los cuales son solicitudes de patente, por lo que es muy posible que se otorguen más patentes.
Y dado que la palabra "galería de símbolos" no produjo ningún acierto en esta página antes de mi respuesta, parece que el subconjunto de la comunidad SO que participó en esta página hasta el momento, a pesar de ser bastante numerosa, desconocía la ausencia de teselas, stencil-buffer- métodos basados en la representación de ruta / fuente en general. Kilgard tiene una publicación similar a la de las preguntas frecuentes en el foro de OpenGL que puede ilustrar cómo los métodos de renderización de ruta libre de teselas difieren de los gráficos 3D estándar de pantano, a pesar de que todavía usan una GPU [GP]. (NVpr necesita un chip compatible con CUDA).
Para una perspectiva histórica, Kilgard es también el autor del clásico "Una API simple basada en OpenGL para Textured Mapped Text", SGI, 1997 , que no debe confundirse con el NVpr basado en esténciles que debutó en 2011.
La mayoría, si no todos, los métodos recientes discutidos en esta página, incluidos los métodos basados en esténciles como NVpr o SDF, como GLyphy (que no voy a analizar más aquí porque otras respuestas ya lo cubren) tienen, sin embargo, una limitación: son adecuado para la visualización de texto grande en monitores convencionales (~ 100 DPI) sin jaggies en cualquier nivel de escalado, y también se ven bien, incluso en tamaño pequeño, en pantallas con alta DPI y retina. Sin embargo, no proporcionan todo lo que Direct2D + DirectWrite de Microsoft le ofrece, es decir, sugerencias de pequeños glifos en las pantallas convencionales. (Para una encuesta visual de sugerencias en general, consulte esta página de typotheque, por ejemplo. Un recurso más profundo está en antigrain.com ).
No conozco ningún producto abierto y productivo basado en OpenGL que pueda hacer lo que Microsoft puede hacer con insinuaciones en este momento. (Reconozco mi ignorancia respecto a las partes internas de OS X GL / Quartz de Apple, porque a mi leal saber y entender, Apple no ha publicado cómo hacen trabajos de renderizado de fuente / ruta basado en GL. Parece que OS X, a diferencia de MacOS 9, no insinúense en absoluto, lo que molesta a algunas personas .) De todos modos, hay un documento de investigación de 2013 que trata la insinuación a través de sombreadores OpenGL escritos por Nicolas P. Rougier de INRIA; probablemente valga la pena leerlo si necesita hacer sugerencias de OpenGL. Si bien puede parecer que una biblioteca como Freetype ya hace todo el trabajo cuando se trata de sugerencias, eso no es así por la siguiente razón, que estoy citando del documento:
La biblioteca FreeType puede rasterizar un glifo usando anti-aliasing sub-pixel en modo RGB. Sin embargo, esto es solo la mitad del problema, ya que también queremos lograr un posicionamiento subpíxel para la ubicación precisa de los glifos. Mostrar el cuadrante texturizado en coordenadas de píxeles fraccionarios no resuelve el problema, ya que solo da como resultado la interpolación de textura en el nivel de píxel completo. En cambio, queremos lograr un cambio preciso (entre 0 y 1) en el dominio subpixel. Esto se puede hacer en un sombreador de fragmentos [...].
La solución no es exactamente trivial, así que no voy a tratar de explicarlo aquí. (El documento es de acceso abierto).
Otra cosa que aprendí del artículo de Rougier (y que Kilgard parece no haber considerado) es que los poderes de las fuentes (Microsoft + Adobe) han creado no uno sino dos métodos de especificación de interletraje. El anterior se basa en la llamada tabla kern y es compatible con freetype. El nuevo se llama GPOS y solo es compatible con bibliotecas de fuentes más nuevas como HarfBuzz o pango en el mundo del software libre. Como NVpr no parece ser compatible con ninguna de estas bibliotecas, es posible que el kerning no funcione de forma automática con NVpr para algunas fuentes nuevas; hay algunos de ellos aparentemente en la naturaleza, de acuerdo con este foro de discusión .
Finalmente, si necesita hacer un diseño de texto complejo (CTL) parece que actualmente no tiene suerte con OpenGL ya que no parece existir una biblioteca basada en OpenGL para eso. (DirectWrite, por otro lado, puede manejar CTL). Hay librerías de código abierto como HarfBuzz que pueden generar CTL, pero no sé cómo conseguirías que funcionen bien (como al usar los métodos basados en esténciles) a través de OpenGL. Probablemente tenga que escribir el código de pegamento para extraer los contornos reconfigurados y alimentarlos en soluciones basadas en NVpr o SDF como rutas.
http://code.google.com/p/glyphy/
La principal diferencia entre GLyphy y otros renderizadores OpenGL basados en SDF es que la mayoría de los otros proyectos muestrean el SDF en una textura. Esto tiene todos los problemas habituales que tiene el muestreo. Es decir. distorsiona el esquema y es de baja calidad. GLyphy en cambio representa el SDF utilizando vectores reales enviados a la GPU. Esto da como resultado una representación de muy alta calidad.
La desventaja es que el código es para iOS con OpenGL ES. Probablemente voy a hacer un puerto OpenGL 4.x para Windows / Linux (con suerte, el autor agregará documentación real).