4 points par GN⁺ 2023-12-08 | 2 commentaires | Partager sur WhatsApp
  • 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 de mask et linearGradient
  • 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 dans SourceAlpha
    • <feColorMatrix type="luminanceToAlpha"> réduit les variations de couleur
    • <feBlend mode="color-burn"> le compose avec le graphique d’origine
  • Le filtre est appliqué à l’élément <use> final avec filter="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 avec stop-opacity="0.6"
    • Aux points 65% et 75%, il utilise du blanc pour ajuster progressivement la transparence
  • 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

 
ndrgrd 2023-12-09

Safari ne déçoit décidément jamais ! Le surnom d’IE des années 2020 ne lui va vraiment pas si mal.

 
GN⁺ 2023-12-08
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

    • Ça ne semble pas près d’arriver. Malheureusement, personne n’a implémenté l’intégralité de la spécification SVG, et avec SVG 2 c’est encore pire
      À 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
    • Sur la page « logo », ceux qui repartent après avoir seulement regardé la forme d’onde de base devraient essayer quelques préréglages dans le menu déroulant en bas à droite
      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

    • En réalité, c’est simplement plus proche du bruit, et plus précisément d’une variante de bruit de Perlin combinée au mode de fusion « color burn ». C’est superposé à un dégradé, donc ça peut rappeler du tramage, mais ce n’en est pas
      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
    • Le tramage est bien une technique réelle apparentée parce que l’effet final peut se ressembler, mais cela ne signifie pas en soi ajouter du grain, et cet exemple n’a pas non plus été produit par tramage
      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
    • Si le but n’est pas de simuler une plage de couleurs plus large avec une palette limitée, je ne sais pas si on peut vraiment appeler ça du tramage
      Ç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...
    • C’est lié au tramage, mais personnellement, ça me paraît plus proche du pointillé [0]
      [0] https://en.wikipedia.org/wiki/Stippling
    • C’est un peu gênant. Je devrais mettre à jour le billet pour ajouter un lien vers cet article
      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

    • Il vaudrait mieux éviter de trop généraliser. Depuis l’arrivée de la vidéo numérique, puis de la photo numérique, il y a toujours eu en parallèle des gens qui cherchaient à réduire le bruit, à l’éliminer, à l’imiter ou à l’utiliser délibérément
      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
    • Le Foveon X3 n’a-t-il pas résolu cela dans une certaine mesure ? D’une manière qui ressemble à l’argentique tout en restant numérique ?
  • 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

    • Je ne suis pas devant mon PC donc je ne peux pas vérifier directement, mais je me demande s’il embarque une seule texture de bruit réutilisée plusieurs fois, ou s’il en recrée une à chaque usage
      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
    • Affinity Designer 2 était plutôt bien comme alternative à Adobe Creative Cloud, mais j’ai vraiment souvent dû retoucher les SVG à la main
  • 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...

    • l’effet apefest consomme beaucoup de CPU, et donc aussi beaucoup d’énergie
  • 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 feDisplacementMap ruinent l’anticrénelage, ce qui gâche environ la moitié des cas où on aurait envie de les utiliser

  • Le 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

    • Même chose ici. J’utilise Android Chrome
    • On dirait qu’Android Chrome ne prend pas en charge une des fonctionnalités utilisées dans le code
  • 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

    • L’auteur ici. Oui, c’est possible. L’algorithme et l’implémentation de base du SVG acceptent une valeur de graine : https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/s...
    • Comme l’a dit une autre réponse, il existe un attribut 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ésultat
      Cela 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...