4 puntos por GN⁺ 2023-12-08 | 2 comentarios | Compartir por WhatsApp
  • A medida que las ilustraciones web se expanden más allá de los vectores planos y adoptan texturas de ruido junto con iluminación y sombreado, se vuelve importante implementar esto en SVG ligeros y reutilizables
  • El objetivo principal es añadir a formas simples un efecto de sombra con textura y convertirlo en componentes de Svelte o React para poder reutilizarlo cambiando colores y formas
  • La estructura base define una forma una sola vez en <defs>, la reutiliza con <use>, y luego construye una máscara con gradiente menos dependiente del color usando mask y linearGradient
  • La sensación granulada se crea con filtros SVG basados en <feTurbulence>, combinando <feComposite>, <feColorMatrix> y <feBlend> para mezclarla con el color de relleno elegido
  • El resultado logró la textura buscada, pero siguen existiendo diferencias de renderizado en Safari, por lo que se necesita ajuste adicional para la representación entre navegadores y su aplicación a ilustraciones más complejas

Añadir textura de ruido a SVG solo con código

  • En el diseño web reciente, las ilustraciones vectoriales siguen usándose, pero destaca un estilo de iluminación y sombreado con granulado áspero en lugar de formas planas y limpias
  • Aunque este estilo puede exportarse como SVG desde programas de ilustración, existe la posibilidad de que la textura entre como ráster o de que el tamaño del vector aumente
  • El objetivo es crear un efecto de sombra texturizada con ruido usando solo la especificación estándar de SVG, manteniendo una estructura fácil de convertir en componentes
  • Está planteado para que el mismo método funcione incluso al cambiar la forma y el color, favoreciendo la reutilización

Forma base y gradiente

  • Las formas básicas de SVG, como círculos o rectángulos, son fáciles de dibujar, pero en componentes reutilizables un gradiente de color fijo no es suficiente
  • Para crear un gradiente que cambie de acuerdo con el color base, se usa una máscara (mask)
    • Las máscaras SVG controlan las áreas visibles u ocultas mediante negro y blanco
    • Aunque el funcionamiento puede resultar algo confuso, se puede empezar con un ejemplo simple
  • Si la estructura usa la misma forma circular varias veces, se define <circle id="shape"> dentro de <defs> y se referencia con <use href="#shape">
  • Este método reduce repeticiones y reúne la definición de la forma en un solo lugar, manteniendo una estructura DRY

Cómo se genera la textura de ruido

  • Para superponer textura sobre el gradiente, se usan filtros (filter) de SVG
  • La generación del ruido corre por cuenta de <feTurbulence>
    • <feTurbulence> es una primitiva de filtro SVG para crear texturas artificiales
    • Para obtener la sensación deseada, hace falta ajustar varios valores
  • El filtro final mezcla la textura con el color a través de varias etapas
    • <feTurbulence type="fractalNoise" baseFrequency="19.5" numOctaves="10"> genera el ruido
    • <feComposite> compone el resultado de turbulence dentro de SourceAlpha
    • <feColorMatrix type="luminanceToAlpha"> reduce las variaciones de color
    • <feBlend mode="color-burn"> lo compone con el gráfico original
  • El filtro se aplica al elemento final <use> con filter="url('#noise')"

Configuración final del SVG

  • El ejemplo final define dentro de <defs> la forma, el filtro, la máscara y el gradiente
  • El gradiente se configura con linearGradient id="fade"
    • En el punto 0% se usa negro con stop-opacity="0.6"
    • En los puntos 65% y 75% se usa blanco para ajustar gradualmente la transparencia
  • A la forma final se le aplican juntos el color de relleno, la máscara y el filtro
    • fill="hsl(337, 92%, 69%)"
    • mask="url(#gradient)"
    • filter="url('#noise')"
  • Al añadir color y ajustar el gradiente de manera más sutil, se obtiene un resultado más cercano al efecto visual deseado
  • El resultado completo puede verse en Full Demo y el código de implementación está publicado en el repository

Resultado y limitaciones

  • Tras experimentos adicionales, se logró una ilustración satisfactoria, aunque el resultado aún no es perfecto
  • En particular, Safari renderiza de forma distinta a otros navegadores
  • Aunque todavía es simple en comparación con la ilustración compleja que sirvió de referencia, con más ajustes podría llegar a usarse en más situaciones
  • Según una actualización del 7 de diciembre de 2023, después de publicarlo en Hacker News, el autor descubrió el artículo de CSS-Tricks de 2021 de Jimmy Chion, Grainy Gradients
  • Ese artículo parece llegar a una técnica similar y puede consultarse también como material complementario

2 comentarios

 
ndrgrd 2023-12-09

¡Safari de nuevo, sin decepcionar las expectativas! De verdad se ha ganado el nombre del IE de los años 20.

 
GN⁺ 2023-12-08
Opiniones de Hacker News
  • Hace poco estuve probando SVG y quise agregar una sombra paralela que me había costado hacer con canvas, pero en Safari la sombra paralela de CSS dentro del SVG no funciona, así que tuve que usar el filtro nativo de sombra paralela de SVG, y el resultado también se veía distinto que en otros navegadores.
    Hay un ejemplo concreto en https://www.einarmagnus.net/pages/logo, y con JavaScript detecto Safari para activar la versión SVG y desactivar la versión CSS.
    El fondo de “yin-yang recursivo” está en https://einarmagnus.net/blog/2020/01/22/finding-the-logo.
    También me gustaría experimentar más con texturas, pero es frustrante que se vea tan diferente en Safari. Me pregunto si algún día convergerá la forma en que se interpreta SVG.

    • Por ahora no parece que vaya a converger. Lamentablemente, no hay ninguna implementación de la especificación completa de SVG, y con SVG 2 la situación queda especialmente mucho más lejos.
      Sobre este tema vale la pena leer https://razrfalcon.github.io/notes-on-svg-parsing/.
      En reportes de bugs de bibliotecas SVG se ve a menudo la frase “mi SVG no se renderiza correctamente”, pero la idea es que, en realidad, no existe tal cosa como un SVG renderizado correctamente. En cuanto empiezas a usar funciones avanzadas como texto, filtros o animación, se rompe fácilmente según el renderizador.
    • Quienes entren a la página de ‘logo’ y solo vean la forma de onda básica deberían probar algunos presets del desplegable de la esquina inferior derecha.
      Es genial cómo, con cambiar apenas los parámetros, salen diseños bastante distintos como scorpion, balance o spruce.
  • El término que buscaba el autor está más cerca de dithering. “Grainy Gradients” de CSS-Tricks también explica la misma técnica.
    https://css-tricks.com/grainy-gradients/
    También hay una alternativa para hacer algo parecido con CSS, aunque se aplica a toda la imagen.
    https://github.com/tomren1/dither-with-css

    • En realidad esto se parece más simplemente a ruido y, con más precisión, a algo de la familia del ruido Perlin combinado con el modo de fusión “color burn”. Al ponerlo encima de un degradado parece dithering, pero no lo es.
      El dithering es una técnica que, al cuantizar una imagen suave de alta profundidad de color a una cantidad limitada de colores, reduce el banding eligiendo probabilísticamente cada píxel entre el valor superior o inferior. En los puntos que corresponden exactamente al color deseado, puede que ni siquiera se vea como ruido.
      Aquí solo se genera una textura de ruido uniforme y no se reduce realmente el banding. Si el objetivo es estético, esto parece mejor que el dithering real.
      Aun así, puede tener el efecto de ocultar defectos y aumentar la nitidez percibida, como el dithering, haciendo que la calidad de la imagen parezca mejor.
    • El dithering es una técnica real cuyo efecto final es parecido, pero en sí no significa agregar granulado, y este ejemplo tampoco está hecho con dithering.
      El autor parece estar buscando un término de diseño para nombrar un estilo visual, como “flat”, “skeuomorphic” o “italian style”.
      Por la combinación de formas geométricas y granulado, me recuerda al Memphis style.
    • Si no se está imitando una gama más amplia de colores con una paleta limitada, no sé si realmente se le puede llamar dithering.
      Más bien me recuerda al arte de pósters de viaje vintage. Ej.: https://www.graphicart-news.com/wp-content/uploads/2012/09/2...
    • Está relacionado con el dithering, pero personalmente me parece más cercano al puntillismo [0].
      [0] https://en.wikipedia.org/wiki/Stippling
    • Es un poco vergonzoso. Tendría que actualizar el artículo para enlazar ese texto.
      Cuando hice esto de verdad no lo había visto, y llegué a la misma conclusión probando por mi cuenta.
  • Me recuerda a cómo los fotógrafos de película y los cineastas se esforzaban por eliminar el grano de película, mientras que los fotógrafos digitales ahora parecen querer volver a agregarlo. El pasto siempre se ve más verde del otro lado.
    Entre tantas cosas digitalmente asépticas, los ejemplos de esta página se ven bastante atractivos.

    • Mejor no generalizar demasiado. Desde la aparición del video digital y luego de la fotografía digital, siempre hubo gente que quería reducir el ruido, gente que quería eliminarlo, gente que quería imitarlo y gente que quería usarlo intencionalmente.
      El ruido en sí no era el problema; la cuestión siempre fue si había demasiado.
    • ¿No lo resolvía en cierta medida Foveon X3? De una forma que se ve como película, pero es digital
  • ¿Habrá algún material que recopile qué funciones se rompen según la implementación? Me interesa cómo cuidar la salud mental con SVG
    Hace poco cometí el error de hacer un póster complejo para una conferencia en SVG, y ahora se renderiza distinto según el navegador y el programa
    https://kxygk.github.io/imergination/
    Esto fue lo que experimenté al hacerlo: se puede incluir o enlazar un SVG, pero si dentro de ese SVG vuelve a haber otro SVG, no funciona. Por ejemplo, si la estructura es póster → diagrama → varios gráficos/mapas, un SVG que se actualice dinámicamente cuando cambian las imágenes secundarias es prácticamente imposible
    Los navegadores web a veces tampoco muestran a su antojo las imágenes embebidas/enlazadas por motivos de “seguridad”. Si abres el SVG del enlace anterior en una pestaña aparte, no se muestra correctamente: https://raw.githubusercontent.com/wiki/kxygk/imergination/ag...
    Convertir SVG complejos a PDF también es difícil de hacer sin inconsistencias, e incluso puede agotar la memoria. Cada convertidor de SVG a PDF da resultados distintos
    Varias implementaciones como Inkscape a veces trataban arbitrariamente los SVG incluidos como gráficos rasterizados, y nunca pude identificar cuándo ocurría eso. También había un problema al renderizar a PNG en el que Inkscape no propagaba la configuración de DPI a las imágenes secundarias

  • Está genial. Parece que Inkscape permite este tipo de agregado de ruido mediante varios efectos del menú Filters. Exporté como Plain SVG y, al revisar el código, confirmé que usa feTurbulence
    Para comparar, también probé el relleno de ruido de Affinity Designer 2, y en el SVG exportado el ruido venía incrustado como una imagen JPEG en base64

    • Ahora no estoy frente a la PC, así que no puedo comprobarlo directamente, pero me da curiosidad si incrusta una sola textura de ruido y la reutiliza varias veces, o si crea una textura nueva cada vez que se usa ruido
      Como me cuesta justificar la licencia de Adobe y InkScape tampoco termina de convencerme, Designer era una alternativa perfecta salvo por estas rarezas
    • Affinity Designer 2 era una opción bastante buena como alternativa a pagar Adobe Creative Cloud, pero de verdad había que retocar SVG a mano muchísimas veces
  • El sitio web de “ApeFest”[1] también tenía ruido dinámico, así que revisé el CSS. Es el sitio de ese evento que les quemó la retina con UV a los asistentes
    En realidad usa una superposición PNG[2] en forma de mosaico y la sacude con una animación CSS para que parezca “aleatoria”. Corrección: en realidad parece que se mueve con JS. Es bastante low-tech, pero funciona
    [1] https://apefest.com/
    [2] https://framerusercontent.com/images/rR6HYXBrMmX4cRpXfXUOvpv...

    • El efecto de apefest consume mucho CPU y, por lo tanto, también mucha energía
  • Quería usar estos filtros SVG muchas veces, pero el problema es que la representación cambia mucho según el motor y el nivel de zoom
    Por eso solo se pueden usar para efectos sutiles donde la imprecisión no importa demasiado, y al final, aunque dé pena, uno termina volviendo constantemente al raster
    Además, por su naturaleza de operar a nivel de píxeles, cosas como feDisplacementMap arruinan el antialiasing, lo que echa a perder más o menos la mitad de las situaciones en las que dan ganas de usarlo

  • El ruido Perlin y su sucesor, el ruido fractal, tienen una relación costo-beneficio enorme cuando se quiere agregar realismo o una sensación orgánica
    Por ejemplo, se puede ver usado de forma muy efectiva en la escena de escape de la Cave of Wonders en Aladdin (1992)

  • No sé cómo se supone que debería verse esto, pero en mi teléfono solo se ve como un gradiente suave, sin ruido

    • Aquí igual. Estoy usando Android Chrome
    • Parece que Android Chrome no soporta alguna función del código
  • Me gusta el efecto, pero ¿habrá alguna forma de hacerlo independiente del nivel de zoom?
    Relacionado con eso, esto también parece agregar aleatoriedad, y me pregunto si hay forma de darle una semilla. Por ejemplo, estaría bueno poder definir un único resultado de renderizado correcto para un tamaño específico

    • Soy el autor. Sí se puede. El algoritmo base de SVG y la implementación aceptan un valor de semilla: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/s...
    • Como dicen en otra respuesta, existe el atributo seed, y el algoritmo de la función aleatoria también está definido en la especificación como 186 líneas de código C[0], así que en teoría debería dar el mismo resultado en distintas implementaciones
      Sin embargo, al mirar el código, usa operaciones de punto flotante, y según entiendo eso queda definido por la implementación, así que parece posible que se comporte de forma distinta según la plataforma
      [0]: https://drafts.fxtf.org/filter-effects/#elementdef-feturbule...