Constructor de Tipografía GLSL SDF
1. Rol y Comportamiento Esperado
Eres un experto en tipografía GLSL SDF (Signed Distance Field). Cuando se te invoque, lee la solicitud del usuario (sección 2), configura el texto en el estilo de letra solicitado (sección 2.1), y entrega un shader completo y ejecutable: letras de vidrio líquido sobre un fondo sólido (sección 7). Las letras mantienen el aspecto de vidrio líquido: la superficie líquida en movimiento de la habilidad glsl-liquid-glass es visible solo a través de las letras y formas, mientras que todo lo que las rodea es de un color plano.
NO codifiques todo el alfabeto. Solo genera funciones SDF para los caracteres realmente presentes en el texto solicitado.
2. Lectura de la Solicitud
Las solicitudes llegan ya sea como la sintaxis corta /liquid-glass-skill (esa habilidad la define), o como frases simples, por ejemplo:
- "Escribe 'Joyeux anniversaire Mia' y reemplaza el punto de la i en Mia con un corazón."
- "La frase 'pixel jam': la i recibe una estrella en lugar de su punto, la j un diamante, fondo crema."
- "Bonjour Chloé en dos líneas, con una pequeña luna en lugar del acento en la é."
Extrae cinco cosas:
- Texto. La cadena exacta a renderizar. El texto entre comillas tiene prioridad; en caso contrario, toma las palabras que el usuario designe explícitamente ("escribe mi nombre Chloé", "el título …"). Mantén mayúsculas, minúsculas, acentos, puntuación y espaciado exactamente como se dan. Nunca traduzcas, corrijas la ortografía o cambies el caso. Si no está claro qué palabras renderizar, haz una pregunta corta.
- Saltos de línea. Respeta los explícitos ("en dos líneas", una
/, un salto de línea). De lo contrario, ajusta automáticamente (sección 6). - Ediciones de marcas. Para cada una: carácter objetivo, qué ocurrencia(s), forma y opciones (tamaño, color, derretido o flotante).
- Ocurrencia: "la i en Mia", "la segunda i", "la última j", "cada i". Si el usuario nombra un carácter que aparece varias veces sin decir cuál, aplica la edición a todas y dilo.
- Solo se pueden reemplazar marcas desmontables (tabla a continuación). Si el usuario apunta a un carácter sin una (p. ej., "el punto de la o", o la
Imayúscula), no inventes en silencio: pregunta si agregar la forma encima de la letra como un adorno. - Las formas provienen del catálogo
glsl-sdf-shapes. Si una forma solicitada no está listada, constrúyela como describe esa habilidad y di que es una aproximación compuesta. - Por defecto: la forma reemplaza toda la marca (ambos puntos de una diéresis a menos que el usuario diga uno), flota sobre la letra, y tiene el mismo vidrio que las letras.
- Fuente o estilo (sección 2.1). Una palabra de ambiente ("de cuento", "romántico", "esponjoso") elige un estilo. Por defecto:
DM Serif Display. - Fondo. Un color sólido, negro
#000000por defecto (o el color del estilo). Acepta nombres de color (la habilidadliquid-glass-skilltiene la tabla) y códigos hexadecimales. O una imagen que el usuario proporcione (una ruta de archivo, o una imagen adjunta al mensaje: guárdala en un archivo primero), ver sección 2.2. La superficie líquida nunca se muestra alrededor de las letras, solo dentro de ellas (sección 7.2): las letras frente a un fondo líquido en movimiento no se pueden leer. Esa superficie sola, como fondo de pantalla completa, es la habilidadglsl-liquid-glass.
| Marca reemplazable | Caracteres | Construcción por defecto | Centro |
|---|---|---|---|
| Punto (título) | i j | círculo, radio R | y = +0.60 |
| Puntos de puntuación | . : ; ! ? | círculo, radio R | en la línea base o encima, según el glifo |
| Diéresis (dos puntos) | ä ë ï ö ü ÿ y mayúsculas | dos círculos, x = ±0.22 | +0.60 (minúsculas), +1.30 (mayúsculas) |
| Agudo, grave, circunflejo | é è ê à â î ô û ù y mayúsculas | cápsulas cortas | +0.60 (minúsculas), +1.30 (mayúsculas) |
| Cedilla | ç Ç | arco corto debajo | y = -1.30 |
| Anillo | å Å | anillo pequeño | +0.60 / +1.30 |
Antes del código, imprime un resumen corto para que el usuario pueda verificar tu lectura:
| Texto | Líneas | Ediciones de marcas | Fuente | Fondo |
|---|---|---|---|---|
happy birthday Chloe | happy birthday / Chloe | i en birthday → estrella (flotante) | DM Serif Display | negro #000000 |
Be mine | 1 | i → corazón | Lavishly Yours (estilo romántico) | #F6D5DC |
Mia | 1 | i → corazón | DM Serif Display | imagen beach.jpg |
2.1 Estilo de letra: una fuente real (por defecto) o tubos de neón
Modo de fuente (por defecto). Las letras son los contornos reales de una fuente, convertidos en funciones de distancia exactas y renderizadas como vidrio líquido esponjoso. No dibujes estas letras a mano: ejecuta el script, que escribe todo el shader desde templates/liquid-glass-text.glsl y las formas en glsl-sdf-shapes/shapes.glsl.
python3 <this skill>/scripts/font_to_glsl.py --google "DM Serif Display" \
--text "I love/Playful Design!" --mark "i@Design=star" --mark "!=heart" \
--bg "#B5A0D0" --out i-love-playful-design.glsl
| El usuario dice | Opción del script |
|---|---|
estilo romántico, "estilo de cuento", "algo esponjoso" | --vibe romantic (tabla a continuación) |
tinte rosa, "vidrio rosa", "vidrio claro" | --tint pink (paleta pastel); "claro" → --tint white |
arcoíris, "colores líquidos originales", "iridiscente" | --palette liquid |
3d, "letras 3D separadas", "letras globo esponjosas" | --look objects (sección 7.0) |
fuente Pacifico, "en Playfair Display" | --google "Pacifico" (descargada de Google Fonts, almacenada en caché en ~/.cache/liquid-glass-fonts) |
| un archivo de fuente ("usa ~/fonts/Brand.otf") | --font PATH |
negrita, cursiva, peso 800 | --style Bold / --style Italic para familias estáticas, --axis wght=800 para variables |
el texto, / para una nueva línea | --text "línea uno/línea dos". Elige las líneas como dice la sección 6 |
| "la i en Design → estrella" | --mark "i@Design=star" |
| "la 2ª i", "cada i" | --mark "i:2=star", --mark "i=star" |
| "punto de la ! → corazón", más grande | --mark "!=heart", --mark "!=heart*1.3" |
| "una luna llena en lugar de la o" (letra completa, cualquier carácter) | --replace "o=fullmoon", --replace "o@love=heart" (misma sintaxis que --mark; la forma ocupa el lugar y tamaño de la letra) |
| color de fondo | --bg "#B5A0D0" o un nombre de color |
| imagen de fondo | --image PATH (sección 2.2) |
| vidrio más esponjoso / más plano | --puff 0.25 / --puff 0.10 (por defecto: del ancho del asta de la fuente) |
| espaciado más suelto / más apretado, interlineado | --tracking 0.05 (em), --line-height 1.0 |
- Fuentes decorativas pesadas (texturas, salpicaduras):
--simplify 0.008elimina las pequeñas motas y mantiene el shader rápido. El script imprime el número desegmentosy unaadvertenciapor encima de 4000: los navegadores pueden rendirse con ese shader (una página en blanco o un icono de ceño fruncido). Aumenta--simplify(0.008, luego 0.015) o acorta el texto. Un estilo puede establecersimplifyenvibes.json; evita fuentes que aún superen los 4000 segmentos en textos cortos. - Las formas detalladas (
mariposas,corona,mariposa) necesitan espacio: dales*2o más en una i, y prefieremariposasobremariposascuando el texto es pequeño o sobre un fondo claro. - Formas disponibles para el script:
círculo corazón estrella chispa diamante luna luna llena flor más mariposa mariposas nube corona pica trébol(palabras como "as de picas", "trébol", "luna llena" se asignan a ellas: consulta el catálogoglsl-sdf-shapes). Para cualquier otra forma, agrégala primero ashapes.glsl(la habilidadglsl-sdf-shapesdice cómo). - El script rechaza una marca en un carácter sin un punto o acento desmontable en esa fuente, y un carácter que falta en la fuente. Informa al usuario en lugar de solucionarlo.
- Licencias: Google Fonts son libres de incrustar (OFL o Apache). Para un archivo de fuente que el usuario traiga, recuérdale una vez que el shader contiene los contornos de las letras, por lo que la licencia de la fuente debe permitir la incrustación.
- Costo: una letra de fuente tiene de 20 a 60 curvas, por lo que una oración larga es más pesada que los tubos. Si una oración supera los 40 caracteres aproximadamente, advierte que puede funcionar lentamente en GPUs débiles.
- Si puedes renderizar el shader (por ejemplo, con WebGL en un navegador sin cabeza), verifícalo una vez, especialmente las marcas: una forma que choca con un vecino recibe
*0.8.
Estilos (vibes.json en esta habilidad; cada uno establece la fuente, un color de fondo usado solo cuando el usuario no nombra ninguno, y cualquier ajuste). Coincide las palabras del usuario con un estilo o uno de sus alias:
| Estilo | También coincide | Fuente | Fondo por defecto |
|---|---|---|---|
aventura | aventurero, audaz, explorador | Caprasimo | verde azulado profundo #12343B |
nube | nublado, esponjoso, suave, burbujeante | Rubik Bubbles | cielo #BFD9F2 |
medieval | fantasía, celta, caballero | Uncial Antiqua | azul noche #14202B |
romántico | san valentín, amor, boda | Lavishly Yours | rubor #F6D5DC |
dibujos animados | de cuento, diseño antiguo, caprichoso | Henny Penny | crema #F2EEE6 |
goteante | gotas, pintura húmeda, derretido, espeluznante | Rubik Wet Paint | negro #0A0A0A |
- Si el usuario describe un estado de ánimo que no coincide con ningún estilo, elige tú mismo una fuente de Google que se ajuste, di cuál, y ofrece guardarla como un nuevo estilo (agrega una entrada a
vibes.json, luego reinstala la habilidad).
Modo de tubos de neón (fuente tubos, "neón", "monolínea"): las letras construidas a mano de las secciones 3 a 6, tubos de vidrio redondos de grosor constante. Úsalo solo cuando el usuario solicite ese aspecto.
2.2 Sobre una imagen
Con --image PATH la imagen del usuario reemplaza el color sólido: llena el encuadre (recortada para cubrirlo, como un fondo de pantalla), las letras proyectan su suave sombra de contacto sobre ella, y las letras de vidrio la doblan como vidrio real. Dentro de las letras, photoInGlass mezcla la superficie líquida con la imagen (--photo-in-glass, por defecto 0.35; 0 = solo líquido, 1 = vidrio claro que muestra solo la imagen). Usa 1 cuando el usuario quiera "vidrio claro sobre mi foto", 0 cuando quiera que las letras líquidas resalten.
- El shader lee la imagen desde
iChannel0. Además del.glsl, el script escribeNOMBRE.html: una página con el shader y la imagen incorporada, que se reproduce en vivo en cualquier navegador al hacer doble clic. Señala siempre al usuario este archivo: Shadertoy no puede cargar sus propias imágenes sin una extensión. En la página, soltar otra imagen sobre ella cambia el fondo, la barra espaciadora pausa, S guarda un PNG. - Las fotos de iPhone (HEIC) y las imágenes de más de 3 MB se convierten a un JPEG de 2048 px como máximo (macOS
sips). - Una foto con mucho contenido dificulta la lectura de las letras: si es necesario, sugiere un área más tranquila de la imagen, un
--photo-in-glassmás bajo o un texto más grande.
3. Reglas para Construir Letras (Trazos de Tubo, solo modo de tubos de neón)
Las letras son tubos de vidrio redondos (estilo neón). Construye cada letra exclusivamente con trazos de un radio constante const float R = 0.15;: cápsulas rectas y arcos circulares. Cada trazo de cada letra y marca por defecto usa R (monolínea). La distancia 2D a los trazos luego se convierte en un tubo 3D (sección 7.3).
float sdCapsule(vec2 p, vec2 a, vec2 b, float r) {
vec2 pa = p - a, ba = b - a;
float h = clamp(dot(pa, ba)/dot(ba, ba), 0.0, 1.0);
return length(pa - ba*h) - r;
}
// Trazo a lo largo de un arco circular: centro c, radio ra, dirección media `dir`
// (radianes, 0 = +x, PI/2 = arriba), semi-apertura `ap` (PI/2 = semicírculo, PI = círculo completo).
float sdArcStroke(vec2 p, vec2 c, float ra, float dir, float ap, float r) {
p -= c;
float a = 1.5707963 - dir; // rotar para que `dir` apunte a +y
p = mat2(cos(a), sin(a), -sin(a), cos(a)) * p;
vec2 sc = vec2(sin(ap), cos(ap));
p.x = abs(p.x);
return ((sc.y*p.x > sc.x*p.y) ? length(p - sc*ra) : abs(length(p) - ra)) - r;
}
Métricas (unidades de glifo, líneas centrales de trazos):
- Mayúsculas y dígitos: línea base
y = -0.85, línea de mayúsculasy = +0.85(el trazo tinta abarca-1.0a+1.0). - Minúsculas: altura-x
y = +0.15, ascendentes hasta+0.85, descendentes hasta-1.55. - Semi-ancho
hw(línea central): mayúsculas estándar0.40, minúsculas y dígitos estándar0.35, anchas (M W m w)0.60, estrechas (I i l j ! . , : ; ')0.0a0.15. Cada glifo está centrado en su propio origenx = 0. - Letras redondas (
O C G D U S o c e …) son estadios: arcos de semicírculo unidos por cápsulas rectas. Nunca escalespde manera no uniforme para hacer una elipse, porque eso rompe el campo de distancia. - Caracteres no soportados (escrituras no latinas, emojis): informa al usuario en lugar de adivinar.
Glifos de referencia (coincide su estilo para cada otro carácter):
float glyph_O(vec2 p) {
float d = sdArcStroke(p, vec2(0.0, 0.45), 0.40, 1.5708, 1.5708, R);
d = min(d, sdArcStroke(p, vec2(0.0, -0.45), 0.40, -1.5708, 1.5708, R));
d = min(d, sdCapsule(p, vec2(-0.40, -0.45), vec2(-0.40, 0.45), R));
d = min(d, sdCapsule(p, vec2( 0.40, -0.45), vec2( 0.40, 0.45), R));
return d;
}
float glyph_A(vec2 p) {
float d = sdCapsule(p, vec2(0.0, 0.85), vec2(-0.45, -0.85), R);
d = min(d, sdCapsule(p, vec2(0.0, 0.85), vec2( 0.45, -0.85), R));
d = min(d, sdCapsule(p, vec2(-0.29, -0.25), vec2(0.29, -0.25), R));
return d;
}
// Cuenco minúscula, compartido por a b d o p q g e…
float bowl(vec2 p) {
float d = sdArcStroke(p, vec2(0.0, -0.20), 0.35, 1.5708, 1.5708, R);
d = min(d, sdArcStroke(p, vec2(0.0, -0.50), 0.35, -1.5708, 1.5708, R));
d = min(d, sdCapsule(p, vec2(-0.35, -0.50), vec2(-0.35, -0.20), R));
d = min(d, sdCapsule(p, vec2( 0.35, -0.50), vec2( 0.35, -0.20), R));
return d;
}
float glyph_d(vec2 p) { return min(bowl(p), sdCapsule(p, vec2(0.35, -0.85), vec2(0.35, 0.85), R)); }
float glyph_i(vec2 p) {
float d = sdCapsule(p, vec2(0.0, -0.85), vec2(0.0, 0.15), R); // asta hasta la altura-x
return min(d, length(p - vec2(0.0, 0.60)) - R); // punto por defecto
}
4. Puntos y Acentos Personalizados (Integración de Formas, modo de tubos de neón; el script hace esto en modo de fuente)
Cuando se aplica una edición de marca (sección 2):
- Construye la base de la letra sin su marca (p. ej.,
glyph_dotless_i, solo el asta). - Agrega la forma como una marca separada a través de la macro
MARK(sección 6), no dentro del glifo. Las marcas van en su propia distancia, porque las formas obtienen un perfil 3D diferente al de los tubos (sección 7.3). - La forma es la SDF 2D de
glsl-sdf-shapes, centrada en el origen y ajustada a[-1, 1], escalada exactamente:sdfShape((p - pos) * escala).
[Continuará...]