Créer des SVG à texture de bruit
(daniel.do)- Alors que les illustrations web dépassent les vecteurs plats pour intégrer des textures de bruit ainsi que des effets de lumière et d’ombre, il devient important de savoir les implémenter sous forme de SVG légers et réutilisables
- L’objectif principal est d’ajouter à des formes simples un effet d’ombre texturé, puis de les porter en composants Svelte ou React afin de pouvoir en modifier les couleurs et les formes
- La structure de base consiste à définir une forme une seule fois dans
<defs>, à la réutiliser avec<use>, puis à construire un masque de dégradé moins dépendant des couleurs à l’aide demasketlinearGradient - Le grain est créé avec un filtre SVG basé sur
<feTurbulence>, puis combiné à la couleur de remplissage choisie avec<feComposite>,<feColorMatrix>et<feBlend> - Le résultat atteint la texture visée, mais des différences de rendu dans Safari subsistent ; des ajustements supplémentaires sont donc nécessaires pour l’affichage selon les navigateurs et l’application à des illustrations plus complexes
Ajouter une texture de bruit à un SVG uniquement avec du code
- Dans le web design récent, les illustrations vectorielles restent largement utilisées, mais les styles d’éclairage et d’ombrage avec un grain rugueux se démarquent davantage que les formes plates et propres
- Même si ce style peut être exporté en SVG depuis un logiciel d’illustration, la texture risque d’être intégrée en raster ou d’alourdir fortement le vecteur
- L’objectif est de créer un effet d’ombre texturé avec du bruit uniquement à partir de la spécification SVG standard, tout en conservant une structure facile à transformer en composant
- La mise en place est pensée pour fonctionner de la même manière même lorsque l’on change la forme ou la couleur, afin de favoriser la réutilisation
Formes de base et dégradé
- Les formes SVG de base comme les cercles ou les rectangles sont faciles à dessiner, mais dans un composant réutilisable, des dégradés à couleurs fixes ne suffisent pas toujours
- Pour créer un dégradé qui s’adapte à la couleur de base, on utilise un masque (mask)
- Un masque SVG contrôle les zones visibles ou masquées au moyen du noir et du blanc
- La méthode peut sembler un peu déroutante, mais on peut commencer par un exemple simple
- Si une même forme de cercle est utilisée plusieurs fois, on définit
<circle id="shape">dans<defs>, puis on la référence avec<use href="#shape"> - Cette approche réduit les répétitions et centralise la définition de la forme dans une structure DRY
Méthode de génération de la texture de bruit
- Pour superposer une texture au dégradé, on utilise un filtre (filter) SVG
- La génération du bruit est assurée par
<feTurbulence><feTurbulence>est une primitive de filtre SVG qui crée des textures artificielles- Il faut ajuster plusieurs valeurs pour obtenir le rendu souhaité
- Le filtre final mélange la texture et la couleur en plusieurs étapes
- Le bruit est généré avec
<feTurbulence type="fractalNoise" baseFrequency="19.5" numOctaves="10"> <feComposite>compose le résultat de turbulence dansSourceAlpha<feColorMatrix type="luminanceToAlpha">réduit les variations de couleur<feBlend mode="color-burn">le compose avec le graphique d’origine
- Le bruit est généré avec
- Le filtre est appliqué à l’élément
<use>final avecfilter="url('#noise')"
Structure finale du SVG
- L’exemple final définit ensemble, dans
<defs>, la forme, le filtre, le masque et le dégradé - Le dégradé est construit avec
linearGradient id="fade"- Au point
0%, il utilise du noir avecstop-opacity="0.6" - Aux points
65%et75%, il utilise du blanc pour ajuster progressivement la transparence
- Au point
- La forme finale reçoit à la fois une couleur de remplissage, un masque et un filtre
fill="hsl(337, 92%, 69%)"mask="url(#gradient)"filter="url('#noise')"
- En ajoutant de la couleur et en affinant davantage le dégradé, on obtient un résultat plus proche de l’effet visuel recherché
- Le résultat complet est visible dans la Full Demo, et le code d’implémentation est publié dans le repository
Résultats et limites
- Après des expérimentations supplémentaires, l’illustration obtenue est satisfaisante, mais le résultat n’est pas parfait
- En particulier, Safari effectue un rendu différent des autres navigateurs
- Par rapport à l’illustration complexe qui a servi de référence, cela reste encore simple, mais avec davantage d’affinage, cette approche pourrait être utilisée dans plus de situations
- Selon un ajout du 7 décembre 2023, après la soumission sur Hacker News, l’auteur a découvert l’article CSS-Tricks de Jimmy Chion publié en 2021, Grainy Gradients
- La technique présentée dans cet article semble proche de cette approche et peut être consultée comme ressource complémentaire
2 commentaires
Safari ne déçoit décidément jamais ! Le surnom d’IE des années 2020 ne lui va vraiment pas si mal.
Commentaires sur Hacker News
En bricolant récemment avec SVG, j’ai voulu ajouter une ombre portée qui était difficile à faire avec canvas, mais dans Safari, les ombres portées CSS à l’intérieur d’un SVG ne fonctionnaient pas, j’ai donc dû utiliser le filtre d’ombre portée natif de SVG, et le résultat avait encore un rendu différent des autres navigateurs
Un exemple concret se trouve sur https://www.einarmagnus.net/pages/logo ; le site détecte Safari avec JavaScript pour activer la version SVG et désactiver la version CSS
L’arrière-plan « yin-yang récursif » est expliqué ici : https://einarmagnus.net/blog/2020/01/22/finding-the-logo
J’aimerais aussi expérimenter davantage avec la texture, mais c’est frustrant de voir à quel point le rendu diffère dans Safari. Je me demande si l’interprétation de SVG finira un jour par converger
À ce sujet, https://razrfalcon.github.io/notes-on-svg-parsing/ vaut le détour
Dans les rapports de bugs de bibliothèques SVG, on voit souvent « mon SVG n’est pas rendu correctement », mais en réalité il n’existe pas vraiment de SVG rendu correctement. Dès qu’on commence à utiliser des fonctionnalités avancées comme le texte, les filtres ou les animations, chaque moteur de rendu casse facilement quelque chose
C’est impressionnant de voir à quel point de petites variations de paramètres produisent des designs très différents, comme scorpion, balance ou spruce
Le terme que cherchait l’auteur est plus proche de tramage. « Grainy Gradients » de CSS-Tricks décrit la même technique
https://css-tricks.com/grainy-gradients/
Il existe aussi une alternative similaire en CSS, mais elle s’applique à l’image entière
https://github.com/tomren1/dither-with-css
Le tramage est une technique où, pour réduire les bandes lors de la quantification d’une image lisse en haute profondeur de couleur vers un nombre limité de couleurs, on choisit probabilistiquement pour chaque pixel la valeur juste au-dessus ou au-dessous. Aux endroits qui correspondent exactement à la couleur voulue, cela peut même ne pas ressembler à du bruit
Ici, cela crée seulement une texture de bruit uniforme et ne réduit pas réellement le banding. Pour un objectif esthétique, cela paraît même plus réussi que du vrai tramage
Cela dit, comme le tramage, ça peut masquer les défauts et augmenter la netteté perçue, donnant l’impression d’une meilleure qualité d’image
L’auteur semble chercher un terme de design pour désigner un style visuel, comme « flat », « skeuomorphic » ou « italian style »
Cette combinaison de formes géométriques et de texture granuleuse fait penser au Memphis style
Ça me fait plutôt penser à l’art des affiches de voyage vintage. Exemple : https://www.graphicart-news.com/wp-content/uploads/2012/09/2...
[0] https://en.wikipedia.org/wiki/Stippling
Je ne l’avais vraiment pas vu quand j’ai fait ça, et j’y suis arrivé seul en expérimentant jusqu’à la même conclusion
Cela me rappelle que les photographes argentiques et les cinéastes faisaient tout pour éliminer le grain de pellicule, alors que les photographes numériques veulent maintenant le rajouter. L’herbe est toujours plus verte ailleurs
Au milieu de toutes ces choses au rendu numériquement aseptisé, l’exemple de cette page est assez séduisant
Le bruit en soi n’a jamais été le problème ; la vraie question a toujours été de savoir s’il y en avait trop
Existe-t-il une ressource qui récapitule les fonctionnalités qui cassent selon les implémentations ? J’aimerais bien savoir comment préserver sa santé mentale avec du SVG
J’ai récemment fait l’erreur de créer un poster de conférence complexe en SVG, et maintenant le rendu diffère selon les navigateurs et les logiciels
https://kxygk.github.io/imergination/
Voici ce que j’ai constaté en le faisant : on peut inclure ou lier un SVG, mais s’il contient lui-même d’autres SVG, cela ne fonctionne pas. Par exemple, avec une structure poster → diagramme → plusieurs graphiques/cartes, un SVG qui se met à jour dynamiquement quand les images enfants changent est en pratique impossible
Les navigateurs web n’affichent pas non plus librement les images intégrées/liées pour des raisons de « sécurité ». Si on ouvre le SVG du lien ci-dessus dans un onglet séparé, il ne s’affiche pas correctement : https://raw.githubusercontent.com/wiki/kxygk/imergination/ag...
Convertir un SVG complexe en PDF sans divergences est également difficile, et on peut aussi se retrouver à court de mémoire. Les convertisseurs SVG-to-PDF donnent des résultats différents
Plusieurs implémentations comme Inkscape traitaient parfois arbitrairement les SVG inclus comme des images matricielles, et je n’ai jamais réussi à déterminer dans quels cas cela arrivait. Il y avait aussi un problème où, lors du rendu en PNG, Inkscape ne propageait pas le réglage DPI aux images enfants
C’est chouette. Inkscape semble prendre en charge ce type d’ajout de bruit via plusieurs effets du menu Filters. En exportant en Plain SVG et en regardant le code, on voit qu’il utilise
feTurbulenceÀ titre de comparaison, j’ai aussi testé le remplissage par bruit d’Affinity Designer 2, et dans le SVG exporté le bruit était intégré sous forme d’image JPEG en base64
Comme il est difficile de justifier une licence Adobe et qu’Inkscape ne me convainc pas vraiment non plus, Designer était une alternative parfaite en dehors de ce genre de bizarreries
Le site web d’“ApeFest”[1] utilise aussi du bruit dynamique, donc j’ai regardé son CSS. C’est le site de l’événement qui a grillé les rétines des participants aux UV
En réalité, il utilise une surcouche PNG en tuiles[2], qu’il secoue via une animation CSS pour donner un aspect « aléatoire ». Correction : on dirait en fait que c’est déplacé en JS. C’est assez low-tech, mais ça marche
[1] https://apefest.com/
[2] https://framerusercontent.com/images/rR6HYXBrMmX4cRpXfXUOvpv...
J’ai voulu réutiliser plusieurs fois ce genre de filtres SVG, mais le problème est que le rendu varie énormément selon le moteur et le niveau de zoom
Du coup, on ne peut les utiliser que pour des effets subtils où l’imprécision n’a pas trop d’importance, et au final, malheureusement, je reviens sans cesse au raster
En plus, comme ils fonctionnent au niveau du pixel, des choses comme
feDisplacementMapruinent l’anticrénelage, ce qui gâche environ la moitié des cas où on aurait envie de les utiliserLe bruit de Perlin et son successeur, le bruit fractal, ont un rapport coût/efficacité énorme pour ajouter du réalisme ou une sensation organique
On peut par exemple le voir utilisé de façon très efficace dans Aladdin (1992), dans la scène de fuite de la Cave of Wonders
Je ne sais pas à quoi c’est censé ressembler au départ, mais sur mon téléphone je ne vois qu’un dégradé lisse, sans bruit
J’aime bien l’effet, mais existe-t-il un moyen de le rendre indépendant du niveau de zoom ?
Dans le même ordre d’idée, je me demande aussi s’il y a un moyen de fournir une graine, puisque cela semble ajouter de l’aléatoire. Par exemple, ce serait bien de pouvoir définir un unique rendu correct à une taille donnée
seed, et l’algorithme de la fonction aléatoire est aussi défini dans la spécification en 186 lignes de C[0], donc en théorie toutes les implémentations devraient produire le même résultatCela dit, en regardant le code, il utilise des opérations en virgule flottante, et il me semble que leur comportement peut dépendre de l’implémentation, donc il est possible que cela varie selon les plateformes
[0]: https://drafts.fxtf.org/filter-effects/#elementdef-feturbule...