Creación de SVG con textura de ruido
(daniel.do)- 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 usandomaskylinearGradient - 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 deSourceAlpha<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>confilter="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 constop-opacity="0.6" - En los puntos
65%y75%se usa blanco para ajustar gradualmente la transparencia
- En el punto
- 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
¡Safari de nuevo, sin decepcionar las expectativas! De verdad se ha ganado el nombre del IE de los años 20.
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.
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.
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
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 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.
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...
[0] https://en.wikipedia.org/wiki/Stippling
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.
El ruido en sí no era el problema; la cuestión siempre fue si había demasiado.
¿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
feTurbulencePara 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
Como me cuesta justificar la licencia de Adobe y InkScape tampoco termina de convencerme, Designer era una alternativa perfecta salvo por estas rarezas
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...
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
feDisplacementMaparruinan el antialiasing, lo que echa a perder más o menos la mitad de las situaciones en las que dan ganas de usarloEl 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
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
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 implementacionesSin 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...