Erstellung von SVGs mit Rauschtextur
(daniel.do)- Da sich Web-Illustrationen über flache Vektoren hinaus in Richtung Rauschtexturen sowie Licht- und Schattierungseffekten weiterentwickeln, wird es wichtiger, diese als leichtgewichtige und wiederverwendbare SVGs umzusetzen
- Das Hauptziel ist, einfachen Formen einen texturierten Schatteneffekt hinzuzufügen und sie so in Svelte- oder React-Komponenten zu übertragen, dass sich Farben und Formen flexibel austauschen lassen
- Die Grundstruktur definiert eine Form einmal in
<defs>, verwendet sie mit<use>wieder und erstellt anschließend mitmaskundlinearGradienteine Gradientenmaske, die weniger stark an bestimmte Farben gebunden ist - Die Körnung wird mit einem auf
<feTurbulence>basierenden SVG-Filter erzeugt;<feComposite>,<feColorMatrix>und<feBlend>werden kombiniert, um sie mit der gewählten Füllfarbe zu mischen - Das Ergebnis erreicht zwar die gewünschte Textur, es bleiben aber Rendering-Unterschiede in Safari, sodass für browserabhängige Darstellung und komplexere Illustrationen weitere Anpassungen nötig sind
SVGs nur mit Code um eine Rauschtextur erweitern
- Im aktuellen Webdesign werden Vektorillustrationen weiterhin häufig eingesetzt, gleichzeitig fallen aber Stilrichtungen mit Licht und Schatten samt grober Körnung stärker auf als saubere, flache Formen
- Solche Stile lassen sich zwar aus Illustrationsprogrammen als SVG exportieren, doch dabei kann die Textur als Raster eingebettet werden oder die Vektordatei stark anwachsen
- Ziel ist es, einen Schatteneffekt mit texturierter Rauschstruktur allein mit der allgemeinen SVG-Spezifikation zu erzeugen und die Struktur so beizubehalten, dass sie sich leicht in Komponenten überführen lässt
- Für die Wiederverwendbarkeit ist der Aufbau so gestaltet, dass derselbe Ansatz auch bei anderen Formen und Farben funktioniert
Grundform und Gradient
- SVG-Grundformen wie Kreise oder Rechtecke lassen sich leicht zeichnen, doch in wiederverwendbaren Komponenten reichen Gradienten mit festgelegten Farben allein nicht aus
- Um einen Gradient zu erzeugen, der sich an die Grundfarbe anpasst, wird eine Maske (mask) verwendet
- Eine SVG-Maske steuert mit Schwarz und Weiß, welche Bereiche sichtbar oder verdeckt werden
- Das Verfahren kann etwas verwirrend wirken, lässt sich aber mit einem einfachen Beispiel beginnen
- Wenn dieselbe Kreisform mehrfach verwendet wird, wird in
<defs>ein<circle id="shape">definiert und mit<use href="#shape">referenziert - Diese Methode reduziert Wiederholungen und hält die Formdefinitionen als DRY-Struktur an einer Stelle zusammen
Erzeugung der Rauschtextur
- Um dem Gradient eine Textur zu überlagern, wird ein SVG-Filter (filter) verwendet
- Die Rauscherzeugung übernimmt
<feTurbulence><feTurbulence>ist ein SVG-Filter-Primitive zur Erzeugung künstlicher Texturen- Um die gewünschte Anmutung zu erhalten, müssen mehrere Werte angepasst werden
- Der finale Filter mischt die Textur in mehreren Schritten mit der Farbe
- Mit
<feTurbulence type="fractalNoise" baseFrequency="19.5" numOctaves="10">wird das Rauschen erzeugt - Mit
<feComposite>wird das Turbulence-Ergebnis innerhalb vonSourceAlphakomponiert - Mit
<feColorMatrix type="luminanceToAlpha">werden Farbvariationen reduziert - Mit
<feBlend mode="color-burn">wird es mit der Originalgrafik zusammengesetzt
- Mit
- Der Filter wird auf das abschließende
<use>-Element mitfilter="url('#noise')"angewendet
Aufbau des finalen SVG
- Das finale Beispiel definiert in
<defs>Form, Filter, Maske und Gradient gemeinsam - Der Gradient wird mit
linearGradient id="fade"aufgebaut- An der Position
0%wird Schwarz mitstop-opacity="0.6"verwendet - An den Positionen
65%und75%wird Weiß verwendet, um die Transparenz schrittweise anzupassen
- An der Position
- Auf die finale Form werden Füllfarbe, Maske und Filter gemeinsam angewendet
fill="hsl(337, 92%, 69%)"mask="url(#gradient)"filter="url('#noise')"
- Durch zusätzliche Farben und feinere Anpassung des Gradients wird ein Ergebnis erzielt, das dem gewünschten visuellen Effekt näherkommt
- Das Gesamtergebnis ist in der Full Demo zu sehen, und der Implementierungscode ist im repository öffentlich verfügbar
Ergebnis und Grenzen
- Nach zusätzlichen Experimenten entstand eine zufriedenstellende Illustration, auch wenn das Ergebnis noch nicht perfekt ist
- Besonders in Safari wird es anders gerendert als in anderen Browsern
- Im Vergleich zu der als Referenz herangezogenen komplexen Illustration ist der Ansatz noch einfach, hat aber mit weiterer Verfeinerung Potenzial für mehr Einsatzszenarien
- Laut einem Nachtrag vom 7. Dezember 2023 wurde nach der Einreichung bei Hacker News der CSS-Tricks-Artikel von Jimmy Chion aus dem Jahr 2021 entdeckt: Grainy Gradients
- Die dort erreichte Technik wirkt ähnlich und kann als ergänzendes Referenzmaterial zusammen herangezogen werden
2 Kommentare
Safari enttäuscht die Erwartungen mal wieder nicht! Der Name des IE der 2020er ist wirklich nicht unverdient.
Hacker-News-Kommentare
Ich habe kürzlich wieder mit SVG herumgespielt und wollte einen Drop Shadow hinzufügen, den ich mit Canvas nur schwer umsetzen konnte. In Safari funktionieren jedoch CSS-Drop-Shadows innerhalb von SVG nicht, sodass ich den nativen SVG-Drop-Shadow-Filter verwenden musste, und selbst dann sah das Ergebnis anders aus als in anderen Browsern
Ein konkretes Beispiel gibt es unter https://www.einarmagnus.net/pages/logo; dort wird Safari per JavaScript erkannt, damit die SVG-Version aktiviert und die CSS-Version deaktiviert wird
Der Hintergrund „rekursives Yin und Yang“ ist hier zu sehen: https://einarmagnus.net/blog/2020/01/22/finding-the-logo
Ich würde auch gern noch mehr mit Texturen experimentieren, aber es ist frustrierend, wie anders es in Safari aussieht. Ich frage mich, ob sich die Interpretation von SVG irgendwann angleichen wird
Zu diesem Thema ist https://razrfalcon.github.io/notes-on-svg-parsing/ lesenswert
In Bugreports für SVG-Bibliotheken liest man oft „mein SVG wird nicht korrekt gerendert“, aber der Punkt ist eigentlich, dass es so etwas wie ein korrekt gerendertes SVG gar nicht gibt. Sobald man fortgeschrittene Funktionen wie Text, Filter oder Animationen verwendet, bricht es je nach Renderer leicht auseinander
Es ist beeindruckend, wie schon kleine Parameteränderungen zu ziemlich unterschiedlichen Designs wie scorpion, balance oder spruce führen
Der Begriff, nach dem der Autor gesucht hat, liegt näher an Dithering. Auch CSS-Tricks erklärt in „Grainy Gradients“ dieselbe Technik
https://css-tricks.com/grainy-gradients/
Es gibt auch eine Alternative, die etwas Ähnliches mit CSS macht, allerdings auf das gesamte Bild angewendet
https://github.com/tomren1/dither-with-css
Dithering ist eine Technik, bei der ein glattes Bild mit hoher Farbtiefe auf eine begrenzte Anzahl von Farben quantisiert wird, um Banding zu reduzieren, indem jeder Pixel probabilistisch auf einen oberen oder unteren Wert gesetzt wird. An Stellen, die dem Zielwert genau entsprechen, muss es dabei nicht einmal wie Rauschen aussehen
Hier entsteht lediglich eine gleichmäßige Rauschtextur, und tatsächliches Banding wird dadurch nicht reduziert. Für ästhetische Zwecke sieht das aber vermutlich besser aus als echtes Dithering
Es kann allerdings ähnlich wie Dithering Mängel kaschieren und die wahrgenommene Schärfe erhöhen, sodass die Bildqualität besser wirkt
Der Autor scheint eher nach einem Designbegriff für einen visuellen Stil zu suchen, so wie „flat“, „skeuomorphic“ oder „italian style“
Bei der Kombination aus geometrischen Formen und Körnung muss ich an den Memphis style denken
Es erinnert mich eher an Vintage-Reiseposter-Kunst. Zum Beispiel: https://www.graphicart-news.com/wp-content/uploads/2012/09/2...
[0] https://en.wikipedia.org/wiki/Stippling
Als ich das gemacht habe, hatte ich ihn wirklich nicht gesehen, und ich bin beim eigenen Herumprobieren unabhängig zum selben Ergebnis gekommen
Das erinnert mich daran, wie Filmfotografen und Filmemacher versucht haben, Filmkorn loszuwerden, während Digitalfotografen es nun wieder hinzufügen wollen. Das Gras ist auf der anderen Seite eben immer grüner
Zwischen all den digital steril wirkenden Dingen wirkt das Beispiel auf dieser Seite ziemlich attraktiv
Das Problem war nie das Rauschen an sich, sondern immer nur, wenn es zu viel davon gab
Gibt es irgendwo eine Übersicht dazu, welche Funktionen in welcher Implementierung kaputtgehen? Ich würde gern wissen, wie man mit SVG seine geistige Gesundheit bewahrt
Ich habe kürzlich den Fehler gemacht, ein komplexes Konferenzposter als SVG zu erstellen, und jetzt wird es je nach Browser und Programm unterschiedlich gerendert
https://kxygk.github.io/imergination/
Was ich beim Erstellen erlebt habe: Man kann SVG einbetten oder verlinken, aber wenn darin wiederum andere SVG stecken, funktioniert es nicht. Wenn man also eine Struktur wie Poster → Diagramm → mehrere Plots/Karten hat, sind SVGs, die sich bei Änderungen der Unterbilder dynamisch aktualisieren, praktisch unmöglich
Webbrowser zeigen eingebettete/verlinkte Bilder aus „Sicherheitsgründen“ teils nach Belieben nicht an. Öffnet man das SVG unter dem obigen Link in einem separaten Tab, wird es nicht korrekt dargestellt: https://raw.githubusercontent.com/wiki/kxygk/imergination/ag...
Auch komplexe SVG konsistent in PDF umzuwandeln ist schwierig, und mitunter geht dabei der Speicher aus. Je nach SVG-to-PDF-Konverter fällt das Ergebnis anders aus
Mehrere Implementierungen wie Inkscape behandelten eingebettete SVG teils willkürlich als Rastergrafik, und ich konnte letztlich nie eingrenzen, wann genau das passiert. Beim Rendern nach PNG gab es außerdem das Problem, dass Inkscape die DPI-Einstellung nicht an Unterbilder weitergab
Sieht gut aus. Inkscape scheint diese Art von Rauschen hinzufügen über mehrere Effekte im Menü Filters zu unterstützen. Ich habe als Plain SVG exportiert und im Code nachgesehen; dort wird tatsächlich
feTurbulenceverwendetZum Vergleich habe ich auch die Rauschfüllung von Affinity Designer 2 getestet, und im exportierten SVG war das Rauschen als base64-JPEG-Bild eingebettet
Da sich eine Adobe-Lizenz schwer rechtfertigen lässt und mir Inkscape auch nicht so recht liegt, war Designer abgesehen von solchen Merkwürdigkeiten eine perfekte Alternative
Auf der Website von „ApeFest“[1] gibt es ebenfalls dynamisches Rauschen, also habe ich mir das CSS angesehen. Das ist die Event-Seite jener Veranstaltung, die den Teilnehmern die Netzhaut mit UV verbrannt hat
Tatsächlich wird ein gekacheltes PNG-Overlay[2] verwendet, das per CSS-Animation „zufällig“ hin- und hergeschüttelt wird. Korrektur: Offenbar wird es in Wirklichkeit per JS bewegt. Ziemlich Low-Tech, aber es funktioniert
[1] https://apefest.com/
[2] https://framerusercontent.com/images/rR6HYXBrMmX4cRpXfXUOvpv...
Ich wollte solche SVG-Filter mehrfach verwenden, aber das Problem ist, dass die Darstellung je nach Engine und Zoomstufe stark variiert
Deshalb taugen sie nur für subtile Effekte, bei denen Ungenauigkeit nicht so wichtig ist, und am Ende kehre ich leider immer wieder zu Rastergrafiken zurück
Außerdem arbeiten sie auf Pixelebene, weshalb Dinge wie
feDisplacementMapdas Antialiasing ruinieren, was ungefähr die Hälfte der Situationen zerstört, in denen man sie eigentlich gern einsetzen würdePerlin-Rauschen und sein Nachfolger, Fraktalrauschen, haben ein enorm gutes Kosten-Nutzen-Verhältnis, wenn man Realismus oder ein organisches Gefühl hinzufügen will
Man sieht das zum Beispiel sehr wirkungsvoll in Aladdin (1992) in der Szene, in der sie aus der Cave of Wonders entkommen
Ich weiß nicht, wie das eigentlich aussehen soll, aber auf meinem Handy sehe ich nur einen glatten Verlauf ohne Rauschen
Der Effekt gefällt mir, aber gibt es eine Möglichkeit, ihn unabhängig von der Zoomstufe zu machen?
Außerdem scheint das hier ebenfalls etwas Zufälligkeit hinzuzufügen, daher würde mich interessieren, ob man auch einen Seed setzen kann. Es wäre zum Beispiel gut, wenn man für eine bestimmte Größe ein einziges korrektes Rendering-Ergebnis definieren könnte
seed, und auch der Algorithmus der Zufallsfunktion ist in der Spezifikation in 186 Zeilen C-Code definiert[0], sodass Implementierungen theoretisch zum gleichen Ergebnis kommen solltenWenn man sich den Code allerdings ansieht, werden Gleitkommaoperationen verwendet, und soweit ich weiß, ist deren Verhalten implementierungsabhängig, daher scheint es möglich, dass es je nach Plattform unterschiedlich läuft
[0]: https://drafts.fxtf.org/filter-effects/#elementdef-feturbule...