Vor ein paar Monaten habe ich meine damaligen Kollegen vor ein Rätsel gestellt: Was habe ich versucht zu tun, als ich dieses Bild produziert habe?
Sieht kaputt aus, oder? Trotzdem kann man es als Kunst bezeichnen. Eine Entwicklerin/Künstlerin hat zum Beispiel schon mal gezielt den Firefox-Rendering-Code kaputt gemacht, um interessante Effekte zu erzielen. Notiz am Rande: Mir fällt gerade auf, dass diese Entwicklerin auch so ein rust-Fan ist und ein paar interessante Artikel geschrieben hat.
In diesem Fall war es aber keine Absicht, und auch nicht das Ergebnis von korrumpierten Daten, sondern einfach die ersten Ergebnisse von code, der noch nicht ganz ausgereift war.
Ursprünglich war der Plan, den Kollegen in einem Kurzvortrag später zu erklären, was ich gemacht habe. Aus verschiedenen Gründen ist daraus aber nichts geworden, und da ich nun nicht mehr dort arbeite, wird das auch so schnell nichts mehr. Ich habe zwar versprochen, mal vobeizuschauen, aber bis ich dazu Zeit finde, können ein paar Monate ins Land gehen.
Also mache ich, was ich sowieso machen wollte, und erkläre das Ganze hier. Und da das hier kein Kurzvortrag ist, kann ich ein bisschen weiter ausholen.
Fordit
Fangen wir mal in der physischen Welt an. Mit „Fordit“, auch bekannt als „motor city agate“ oder „Detroit agate“.
Was ist Fordit? Fordit ist im Prinzip eine Art künstlicher Schmuckstein. In vergangenen Jahrzehnten, bevor die Produktionsmethoden optimiert wurden, ist beim Lackieren von Autos immer ein bisschen Farbe auf den Geräten drumherum gelandet. Die wurde dann zusammen mit den Autoteilen gehärtet. Beim nächsten Auto wurde vielleicht eine andere Farbe verwendet, und so sammelte sich Farbschicht um Farbschicht an.
Irgendwann wurde die Farbe zu dick, dann wurde sie abgebrochen und weggeworfen. Irgendwann sind Leute dann auf die Idee gekommen, diese Lackstücke zu polieren, wobei hübsche Schmucksteine herauskamen. Mehr Infos gibt es z.B. hier. Mittlerweile gibt es einige Leute, die Geld damit verdienen, das Zeug auf Müllkippen auszugraben, zu polieren und als Schmuck zu verkaufen (Bilder werde ich hier keine zeigen, das ist mit mit dem Urheberrecht jetzt zu kompliziert. Hinter dem Link oben findet man ein paar Bilder, ansonsten kann man im Web einfach nach „fordite“ suchen).
Der Sprung ins Digitale
Meine Idee war: Das sieht alles sehr hübsch aus. Vielleicht kann ich ja mit einem Computerprogramm einen ähnlichen Effekt erzielen. Die Idee ist simpel: Schichte in irgendeiner Weise Farben übereinander, dann „poliere“ den Stein, indem eine Höhenkarte darübergelegt wird und an jeder Stelle nur der Farbwert der entsprechenden Höhe genommen wird, und das Ergebnis wird schon irgendwie ordentlich aussehen.
Natürlich wäre das dann keine 3D-Darstellung, sondern eine Projektion, ao als ob man den polierten Fordit von oben fotografiert. Man kann sich das Ergebnis nicht aus verschiedenen Perspektiven anschauen. Das wäre für die Zukunft vielleicht auch interessant, ist aber nicht das, was ich hier machen wollte bzw. gemacht habe.
Wie sieht das Ergebnis aus? Nun hier erst einmal zwei Beispiele.
Vorgehensweise
Grundidee
Also, wie habe ich es angestellt? Gehen wir mal Schritt für Schritt vor. Als erstes werden Farben übereinander geschichtet. Im einfachsten Fall einfach in waagerechten Ebenen, zu sehen hier im Querschnitt:
Dann wird „geschliffen“. Für dieses Programm habe ich das mit einer Höhenkarte gemacht, also einer Funktion, die für jeden Punkt eine Höhe angibt. Alle Farbe, die über dieser Höhe ist, wird entfernt. Hier im Querschnitt ist zu sehen, dass der höchste Punkt der Höhenkarte über der höchsten Farbschicht liegt. In diesem Fall wird keine Farbe entfernt und die lila Farbschicht liegt oben:
Dann wird daraus ein Bild erstellt. Da ich keinen 3D-Krams mache, habe ich mich für den einfachsten Weg entschieden: Eine Projektion auf die (x,y)
-Ebene. Oder mit anderen Worten: Ich schaue von oben auf die Farbschichten und nehme immer die Farbe, die oben liegt. Da es eine Rastergrafik wird, kann es natürlich sein, dass durch die Abtastrate einzelne Farben nicht vorkommen, obwohl sie eigentlich an der Oberfläche liegen:
Plaudern aus dem Nähkästchen
Natürlich ist es relativ langweilig, waagerechte Ebenen zu nehmen. Da könnte man auch einfach nur die Höhenkarte nehmen und jeder Höhe eine Farbe zuordnen. Die Farbe hängt dann nur von der Höhe ab und nicht von der Position im Bild. Wenn man zum Beispiel einen Paraboloid als Höhenkarte nimmt, sieht das z.B. so aus:
Also braucht man andere Farbschichten. Naheliegend ist ja, die Farbschichten nicht waagerecht, sondern schräg anzuordnen (senkrecht wäre ja auch doof, weil dann die Farbe nur von der Position abhängen würden und überhaupt nicht von der Höhenkarte):
Das Ergebnis (wieder mit einem Paraboloid als Höhenkarte) sieht allerdings nicht viel interessanter aus:
Wie kriege ich also interessante Bilder hin? Ich brauche nichtlineare Farbschichten! Vielleicht könnte man ja einfach einen Haufen Farbkleckse übereinander schichten. Also keine ganzen Ebenen, sondern verschiedenfarbige Kreise, die an zufälligen Positionen mit zufälligem Radius auftauchen und sich teilweise überlappen. So wie in diesem Schema dargestellt, nur mit mehr Kreisen:
Stellt sich heraus: Das ist mit meiner Vorgehensweise eine ganz schlechte Idee. Daraus ist dieses Bild entstanden, dass ich ganz am Anfang gezeigt habe:
Was ist passiert? Nun, je nachdem wie viele Kreise an verschiedenen Stellen unter einem Kreis liegen, ist dieser Kreis an vielen Stellen auf unterschiedlichen Ebenen. Die Höhenkarte ist in diesem Fall eine einfache Schräge, links ist der tiefste Punkt, rechts der höchste Punkt.
Links kann man noch Kreise erkennen, hauptsächlich daran, dass sich Farben entlang von Kreislinien spontan verschieben. Diese Kreislinien gehören zu Kreisen, die mehr oder weniger ganz verdeckt sind, aber ihre Auswirkungen sieht man bis ganz nach oben.
Rechts ist die Höhenkarte über der höchsten Farbschicht, es wird also nur die oberste Schicht gezeigt. Deswegen kann man hier die Kreise am besten sehen.
In der Mitte aber ist es ein Gewusel an verschiedensten Farben, weil eine Farbschicht auf verschiedenste Höhen verteilt wurde. Das sieht nicht so aus, wie ich wollte.
Die Rettung naht
Ich habe dann noch einen anderen Versuch mit Kreisen gemacht, bei dem immer eine ganze Ebene eingefärbt wurde, kreisföfmig in einer Farbe, sonst in einer anderen. Damit ist z.B. oben das orangerote Bild erstanden. Sieht ein bisschen interessanter aus, aber immer noch nicht das, was ich wollte.
Ein weiterer Versuch war, abwechselnd immer nur eine Hälfte des Bildes einzufärben. Also erst die linke Hälfte, dann die obere Hälfte, dann die rechte Hälfte, dann die untere Hälfte, dann wieder die linke, und so weiter.
Auch das führt zu interessanten Bildern, in diesem Fall ist die Höhenkarte einfach eine schiefe Ebene, die von oben links nach unten rechts ansteigt:
Das Problem: Das sieht zu künstlich aus. Zu viele scharfe Kanten, zu viele Brüche. Also zurück ans Reißbrett.
Kugeln
Die nächste Idee, und vorläufig die, die am besten funktioniert, waren Halbkugeln. Eine farbige Halbkugel an einem Punkt, dann Schichtweise weitere Farbschichten halbkugelförmig drum herum.
Das Problem: Mit den Paraboloiden als Höhenkarte sieht das wieder nur ziemlich langweilig aus. Im großen und Ganzen nämlich immer noch nur nach Kreisen. Wenn man genau hinschaut, kann man sehen, wie die Kreise ein bisschen verzerrt sind, aber das war es auch schon. Ich füge jetzt nicht noch ein Bild dazu ein, aber ihr seid dazu eingeladen, das selbst auszuprobieren.
Mit einer Sinuskurve jedoch sieht das schon ganz anders aus. So ist das grüne Bild oben entstanden.
Konfigurationsdateien
Es gehen also eine Menge Parameter in jedes Bild. Wie die Farben geschichtet sind, welche Höhenkarte verwendet wird, wie die Höhenkarte verschoben und ausgerichtet wird, welche Farben verwendet werden, ggf. ein Seed für den RNG…
Zunächst, um überhaupt erst einmal zu probieren, was funktioniert, habe ich diese ganzen Parameter hardcodiert. Das skaliert natürlich nicht. Also habe ich das Ganze Umgebaut, so dass es jetzt Dateien mit Konfigurationen lesen kann. Schneller, sauberer und man kann sich Konfigurationen, die gefallen, abspeichern und wieder verwenden.
Animationen
Nachdem die Grundlagen liefen, kam mir die nächste Idee: Ich konnte dem Programm Konfigurationsdateien füttern. Warum also nicht einen Haufen von Konfigurationen erstellen (mehrere Konfigurationen pro Datei sind möglich) und in jeder Konfiguration einen Parameter gegenüber der vorherigen Konfiguration leicht ändern?
Das Ganze dann als Video zusammenfügen und… schaut euch das Ergebnis auf Youtube an. Ich habe Halbkugelförmige Farbschichten und eine Sinushöhenkarte genommen. Die Höhenkarte rotiert und verschiebt ihre Phase, so kommt diese Bewegung zustande.
Repositories
Der Code dazu (natürlich rust) ist auf Gitlab zu finden. Das Ganze ist nicht besonders ordentlich geschrieben, weil es hauptsächlich ein bisschen Herumgebastel war. Es ist aber recht klein, sollte also überschaubar sein. Wichtig: Da ich noch einiges an Verbesserungspotential sehe, kann ich keine Garantie dafür geben, dass irgendeine Konfigurationsdatei, die irgendjemand schreibt, in einer späteren Version vergleichbare Ergebnisse bringt, oder auch nur eingelesen werden kann.
Die Dokumentation lässt noch zu wünschen übrig, aber es gibt ein paar Beispielkonfigurationen, an denen zumindest jeder Parameter irgendwo benutzt wird.
Für die meisten fordite-Beispiele, die ich hier gezeigt habe, liegen die Konfigurationsdateien in einem weiteren git-Repo, im Unterordner blogpost_fordite
.
In beiden Repos habe ich die Version, mit der die Bilder für diesen Post erstellt wurden, mit stu-blogpost
getagged. Die Bilder hier sollten also reproduzierbar sein, man muss nur eine alte Version auschecken.
Fazit
Im Großen und Ganzen kann man mit fordite
also schöne Bilder und Animationen erstellen. Nicht ganz das, was ich mir vorgestellt habe, als ich damit anfing, aber immerhin.
Echte Fordite-Schmucksteine gefallen mir besser, dort bilden die Farben noch interessantere Muster. Aber eine Sache kann ich hier machen, die man mit echtem Fordite nicht machen kann: Animationen.
Ausblick
Auf jeden Fall besteht noch viel Potential für Erweiterungen. Hier nur eine kurze Auflistung von Ideen:
- ungleichmäßige Dicke von Farbschichten
- mehr verschiedene Höhenkarten
- mehr Konfigurierbarkeit von Höhenkarten (Streckung, Scherung, ggf. spezifische Konfigurationen für einzelne Höhenkartentypen)
- Höhenkarten nach beliebigen Funktionen (erfordert natürlich einen Parser für die Funktion)
- Höhenkarten aus Bildateien (Grauwertbildern) auslesen (könnte Schwierigkeiten mit Rotieren, Verschieben, Strecken und Scheren verursachen)
- mehr Konfigurierbarkeit von Farbschichten (z.B. könnte man aus den Halbkugelfarbschichten Kugelfarbschichten mit frei wählbarem Mittelpunkt machen)
- mehr Schemate für Farbschichten (z.B. Zylinderförmige Schichten)
- fest vorgegebene Farbreihenfolge (anstatt zufällige Auswahl aus einer Palette)
- komplett zufällige Farben
Es gibt also noch jede Menge Ideen.
Nachwort
Wie schon früher erwähnt habe ich ja einen kleinen Kompressionsfetisch. Alle Fordite-Bilder hier wurden als verlustfreie WebP encodiert. Das ging sehr gut, weil jedes Bild nur eine kleine Farbpalette hat. Hier mal eine Beispielhafte Aufstellung für eines der Bilder, und zwar die grüne Halbkugel mit Sinus-Höhenkarte:
- PNG, Standardkompression des
image
-crates: 569 kiB - PNG, mit
optipng
komprimiert: 71 kiB - PNG mit
zopflipng
komprimiert: 66 kiB - verlustfreies WebP: 60 kiB
zopflipng
braucht natürlich immer noch ewig, und auch optipng
ist nicht gerade schnell. Die WebP-Encodierung hingegen braucht kaum Zeit.
Das Lustige hier ist, dass die Dateien tatsächlich größer werden, wenn ich versuchen würde, sie verlustbehaftet (als JPEG oder verlustbehaftetes WebP) zu komprimieren. Das liegt natürlich daran, dass diese Dateien einfach so gut geeignet sind, sie verlustfrei zu komprimieren, dass es nicht viel besser geht.
Noch ein Nachwort: Mit den ganzen Bildern, Grafiken und den real-life-Herausforderungen hat es mich zwei Wochen gekostet, diesen Blogpost zu schreiben.