the last missing german chapters

pull/116/head
MITI67 8 years ago
parent 50b893cc98
commit fa9f7d8322

@ -0,0 +1,202 @@
![](dragonfly.jpg)
## Zelluläres Rauschen
Im Jahre 1996, 16 Jahre nach Veröffentlichung von Perlins ursprünglichem Noise-Algorithmus und fünf Jahre vor der Erfindung des Simplex Noise-Algorithmus, schrieb Steven Worley einen Artikel mit dem Titel [„Eine Basisfunktion für zelluläre Texturen“](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Darin beschreibt er eine Technik zur prozeduralen Texturierung von Flächen, die heute im Bereich der Computergrafik nicht mehr wegzudenken ist.
Um das Prinzip hinter diesem Verfahren zu verstehen, müssen wir die Abläufe aus dem Blickwinkel von **Iterationen** betrachten. Als Programmierer kannst Du Dir vielleicht denken, was das bedeutet: Ja, genau, es geht um Schleifen und um den Einsatz des ```for```-Befehls. Allerdings gibt es da einen wichtigen Aspekt in GLSL: Die Anzahl der Schleifendurchläufe muss durch eine Konstante (```const```) vorgegeben sein. Sie ist also nicht dynamisch, sondern steht grundsätzlich von vornherein fest.
Lass uns dazu am besten ein Beispiel anschauen.
### Punkte für ein Distanzfeld
Zelluläres Rauschen basiert auf Distanzfeldern, konkret auf der Berechnung der Entfernung zum nächstgelegenen Bezugspunkt aus einer gegebenen Menge von Punkten. Lass uns annehmen, wir wollten ein Distanzfeld aus vier Punkten erzeugen. Was benötigen wir dafür? Nun, **für jeden zu berechnenden Bildpunkt auf unserer Zeichenfläche wollen wir die Entfernung zum nächstgelegenen der vier Bezugspunkte berechnen **. Das bedeutet, dass wir alle vier Bezugspunkte durchlaufen, ihre Entfernung zum aktuell bearbeiteten Pixel berechnen und uns die kleinste dieser Entfernungen merken müssen.
```glsl
float min_dist = 100.; // speichert die kleineste Entf. zu einem der 4 Bezugspunkte
min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
```
![](cell-00.png)
Diese Lösung ist natürlich nicht besonders elegant, aber sie erfüllt ihren Zweck. Lass uns das Ganze nun mit Hilfe eines Arrays und einer ```for```-Schleife realisieren.
```glsl
float m_dist = 100.; // speichert die minimale Entfernung
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
```
Beachte, wie wir hier die ```for```-Schleife einsetzen, um ein Array mit Bezugspunkten zu durchlaufen, und die bislang kleinste Entfernung mit Hilfe der [```min()```](../glossary/?search=min)-Funktion festhalten. Hier folgt eine vollständige Umsetzung dieses Ansatzes in GLSL.
<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
In dem obigen Shader wird einer der Bezugspunkte des Distanzfelds auf die Mausposition über der Zeichenfläche gesetzt. Fahre mit der Maus über die Fläche und spiele ein wenig damit herum. So bekommst Du am besten eine Vorstellung davon, wie der Code funktioniert. Probiere anschließend folgendes aus:
- Gelingt es Dir, die Position der anderen Bezugspunkte zu animieren?
- Nachdem Du [das Kapitel über Formen](../07/?lan=de) ja vermutlich bereits gelesen hast, stelle Dir einen interessanten Weg vor, was man mit dem vorliegenden Distanzfeld anstellen könnte.
- Was muss man tun, um das Distanzfeld um weitere Bezugspunkte zu erweitern? Wie können wir dynamisch einzelne Bezugspunkte hinzufügen oder entfernen?
### Kachelung und Wiederholung
Vielleicht ist Dir schon klargeworden, dass ```for``` Schleifen und *Arrays* nicht die besten Freunde von GLSL sind. Wie schon gesagt: Schleifen in GLSL akzeptieren keine variablen Limite für die Anzahl der Durchläufe. Außerdem verlangsamen vielfache Schleifendurchläufe die Ausführung Deiner Shader spürbar, da Schleifen nicht vorzeitig beendet werden können. Das hat zur Folge, dass dieser Ansatz für Distanzfelder mit einer Vielzahl von Bezugspunkten untauglich ist. Wir müssen einen anderen Weg wählen, einen, der einen Vorteil aus der Parallelverarbeitung der GPU zieht.
![](cell-01.png)
Ein Ansatz, um sich dieser Herausforderung zu stellen, ist die Unterteilung der Zeichenfläche in einzelne Kacheln bzw. Zellen. Nicht jeder Pixel muss die Entfernung zu allen Punkten des Distanzfeldes überprüfen. Manche sind auf jeden Fall zu weit entfernt, um ein Minima zu liefern. Dies gilt beispielsweise für alle Zellen, die nicht direkt an die aktuelle Zelle angrenzen.
Weil die Farbe für jeden Pixel in einem eigenen Thread berechnet wird, können wir die Zeichenfläche in einzelne Zellen unterteilen - jede mit einem Bezugspunkt.
Um Anomalien an den Schnittflächen zwischen den Zellen zu vermeiden, müssen wir jeweils die Entfernung zum Bezugspunkt der benachbarten Zellen überprüfen. Das ist im Wesentlichen die brillante Idee hinter dem [Ansatz von Steven Worley](http://www.rhythmiccanvas.com/research/papers/worley.pdf).
Letztendlich muss jeder Pixel nur die Entfernung zu neun Bezugspunkten berechnen: Den seiner eigenen Zelle und jene der acht umliegenden Zellen. Alle anderen Zellen sind zu weit entfernt.
Wir haben bereits in den Kapiteln über [Muster](../09/?lan=de), [Generative Designs](../10/?lan=de) und [Rauschen](../11/?lan=de) gesehen, wie man die Zeichenfläche in einzelne Zellen unterteilt, von daher bist Du mit diesem Prinzip wahrscheinlich schon vertraut.
```glsl
// den Raum aufblaehen ...
st *= 3.;
// ... und in Zellen unterteilen
vec2 i_st = floor(st);
vec2 f_st = fract(st);
```
Also, wie lautet der Plan? Wir werden die Koordinate der Kachel/Zelle (die in dem Integer-Vektor ```i_st``` gespeichert ist) nutzen, um einen zufälligen Punkt zu erzeugen. Wir setzen dafür die ```random2f```-Funktion ein, die einen ```vec2``` als Parameter erhält und einen ```vec2``` mit einer Zufallsposition zurückliefert. So erhalten wir für jede Zelle einen Bezugspunkt mit einer zufälligen Position innerhalb der Zelle.
```glsl
vec2 point = random2(i_st);
```
Der jeweils zu zeichnende Bildpunkt innerhalb der Zelle (gespeichert in dem Fließkommavektor ```f_st```) wird seine Entfernung zu diesem zufällig gewählten Bezugspunkt berechnen.
```glsl
vec2 diff = point - f_st;
float dist = length(diff);
```
Das Ergebnis sieht dann wie folgt aus:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Aber wir wollen ja zusätzlich noch die Entfernung zu den Bezugspunkten in den umliegenden Zellen einbeziehen. Dafür müssen wir diese Zellen **durchlaufen**. Aber nicht alle, sondern nur die unmittelbar angrenzenden. Das heißt die Zellen mit den Abständen von ```-1``` (links) bis ```1``` (rechts) entlang der ```x```-Achse, sowie die Zellen mit den Abständen von ```-1``` (unten) bis ```1``` (oben) entlang der ```y```-Achse. Dieser Bereich von 3x3 Zellen lässt sich leicht mit Hilfe einer doppelten ```for```-Schleife abarbeiten, so wie im Folgenden gezeigt:
```glsl
for (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// benachbarte Zelle innerhalb des Rasters
vec2 neighbor = vec2(float(x),float(y));
...
}
}
```
![](cell-02.png)
Nun können wir die Bezugspunkte aus jeder der benachbarten Zellen in unserer doppelten ```for```-Schleife berechnen, indem wir den Versatz aus der Variablen ```neighbour``` zu den Koordinaten der aktuellen Zelle addieren.
```glsl
...
// Zufallsposition von der aktuellen + der benachbarten Zelle im Raster
vec2 point = random2(i_st + neighbor);
...
```
Dann bleibt nur noch, die Entfernung vom aktuell zu zeichnenden Punkt zu dem jeweiligen Bezugspunkt zu berechnen und die geringste Entfernung in der Variablen ```m_dist``` (für „minimale Distanz“) zu speichern.
```glsl
...
vec2 diff = neighbor + point - f_st;
// Entfernung zu diesem Punkt
float dist = length(diff);
// die kleinste Entfernung speichern
m_dist = min(m_dist, dist);
...
```
Der obige Programmcode wurde durch einen [Artikel von Inigo Quilez](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm) inspiriert. Dort schreibt er:
„ ... es ist vielleicht interessant darauf hinzuweisen, dass in dem obigen Code ein netter Trick steckt. Die meisten Implementationen dieses Algorithmus leiden unter einer schlechten Präzision der Berechnungen, weil sie die zufälligen Bezugspunkte auf den gesamten Koordinatenraum beziehen, so dass die Koordinaten sehr weit vom Ursprung entfernt sind. Man kann dagegen ansteuern, indem man Variablentypen mit besonders hoher Genauigkeit verwendet, was sich jedoch negativ auf die Geschwindigkeit der Berechnungen auswirkt. Oder man macht es etwas cleverer, indem man die Koordinaten nicht auf den gesamten Koordinatenraum bezieht, sondern auf die Ebene der einzelnen Zellen: Sobald der ganzzahlige Teil und der Nachkommateil des zu zeichnenden Punktes berechnet sind und dadurch die Zelle feststeht, in der sich der Punkt befindet, beschäftigen wir uns nur noch damit, was um diese Zelle herum geschieht. Dadurch müssen wir uns nicht mehr um den ganzzahligen Teil der Koordinaten kümmern, wodurch man viele Bits bei den weiteren Berechnungen einspart. Tatsächlich steuern bei herkömmlichen Voronoi-Implementierungen die ganzzahligen Anteile der Punktkoordinaten ebenfalls dem Wert 0 entgegen, sobald die zufälligen Bezugspunkte der Zellen vom aktuell zu zeichnenden Punkt abgezogen werden. In der obigen Implementation lassen wir es gar nicht erst so weit kommen, weil wir alle Koordinatenberechnungen auf den Raum der Zellen beziehen. Mit diesem Trick kann man sogar einen ganzen Planeten mit derartig geformten Voronoi-Zellen überziehen, indem man die Punktkoordinaten einfach in doppelter Fließkommagenauigkeit darstellt, die Berechnungen von ```floor()``` und ```fract()``` durchführt, und dann mit einfacher Fließkommagenauigkeit fortfährt. So erspart man sich den (Zeit-) Aufwand, die gesamte Berechnung mit doppelter Fließkommagenauigkeit auszuführen. Natürlich kann man diesen Trick auch auf Perlins Noise-Algorithmus anwenden (allerdings habe ich noch nie ein solche Implementation gesehen). “
Um es noch einmal zusammenzufassen: Wir unterteilen den Raum in einzelne Zellen. Für jeden zu zeichnenden Punkt berechnen wir die kleinste Entfernung zum Bezugspunkt seiner Zelle bzw. zu den Bezugspunkten der umliegenden acht Zellen. Als Ergebnis erhalten wir ein Distanzfeld, so wie in dem folgenden Beispiel:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Experimentiere damit, indem Du:
- den Raum in mehr Zellen aufteilst.
- Dir andere Möglichkeiten ausdenkst, um die Bezugspunkte zu animieren.
- einen der Bezugspunkte durch die aktuelle Mausposition ersetzt.
- über andere Wege nachdenkst, um das Distanzfeld zu berechnen, abseits von ```m_dist = min(m_dist, dist);```.
- untersuchst, welche anderen interessanten Muster sich über dieses Distanzfeld erzeugen lassen.
Man kann diesen Algorithmus nicht nur aus der Perspektive der jeweils zu zeichnenden Pixel betrachten, sondern auch aus Sicht der Bezugspunkte. In diesem Fall lässt sich der Algorithmus so beschreiben: Jeder Bezugspunkt wächst aus seiner Mitte heraus, bis er an die Grenzen eines anderen wachsenden Bezugspunktes stößt. Das spiegelt das Wachstum biologischer Zellen in der Natur wieder. Lebendige Organismen werden von dieser Spannung zwischen dem inneren Antrieb zum Wachstum und äußeren Beschränkungen geformt. Der klassische Algorithmus, der dieses Verhalten nachahmt, ist nach [Georgi Feodosjewitsch Woronoi, engl „Georgy Voronoi“](https://de.wikipedia.org/wiki/Voronoi-Diagramm) benannt.
![](monokot_root.jpg)
### Der Voronoi-Algorithmus
Die Erzeugung von Voronoi-Diagrammen auf Basis von zellulärem Rauschen ist weniger kompliziert, als es vielleicht erscheint. Wir müssen nur zusätzliche Informationen über den Bezugspunkt *festhalten*, der dem zu zeichnenden Punkt am nächsten liegt. Dafür verwenden wir eine Variable vom Typ ```vec2``` mit dem Namen ```m_point```. Darin speichern wir den Vektor zum nächstgelegenen Bezugspunkt und nicht einfach nur dessen Entfernung. So behalten wir ein eindeutiges Identifikationsmerkmal für diesen Punkt.
```glsl
...
if( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
```
Bitte beachte, dass wir in dem folgenden Programmcode die geringste Entfernung nicht mehr mit Hilfe der ```min```-Funktion berechnen, sondern einen herkömmlichen ```if```-Befehl einsetzen. Warum wir das tun? Weil wir diesmal etwas mehr unternehmen wollen, sobald ein neuer näherliegender Punkt auftaucht, nämlich seine Position speichern (*Programmzeilen 32 bis 37*).
<div class="codeAndCanvas" data="vorono-00.frag"></div>
Du wirst sehen, dass die Farbe der beweglichen Zelle (die dem Mauszeiger folgt) auf Basis ihrer Position wechselt. Die Ursache dafür ist, dass hier die Farbe aufgrund des Wertes (der Position) des nächstgelegenen Bezugspunktes zugewiesen wird.
Genau wie zuvor ist es nun an der Zeit, das Ganze zu erweitern, indem wir zu dem Algorithmus aus dem [Papier von Steven Worley](http://www.rhythmiccanvas.com/research/papers/worley.pdf) übergehen. Versuche doch einmal selbst, diesen Algorithmus zu implementieren. Du kannst dabei auf das folgende Beispiel zurückgreifen, indem Du darauf klickst.
Bitte beachte, dass der ursprüngliche Ansatz von Steven Worley eine variable Anzahl von Bezugspunkten für jede Zelle vorsieht. In seiner Implementation des Algorithmus in C nutzt er dies für einen zeitigen Abbruch der Schleife. Schleifen in GLSL erlauben jedoch keinen vorzeitigen Ausstieg oder eine variable Anzahl von Schleifendurchläufen, deshalb wirst Du vielleicht besser bei einem Bezugspunkt pro Zelle bleiben.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Sobald Du die Funktionsweise dieses Algorithmus verstanden hast, kannst Du über interessante und kreative Einsatzmöglichkeiten nachdenken.
![Extended Voronoi - Leo Solaas (2011)](solas.png)
![Cloud Cities - Tomás Saraceno (2011)](saraceno.jpg)
![Accretion Disc Series - Clint Fulkerson](accretion.jpg)
![Vonoroi Puzzle - Reza Ali (2015)](reza.png)
### Verbesserung des Voronoi-Algorithmus
Im Jahre 2011 hat [Stefan Gustavson eine Optimierung von Steven Worleys Algorithmus für GPUs](http://webstaff.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf) vorgeschlagen, bei der nur noch eine 2x2 Matrix benachbarter Zellen untersucht wird, an Stelle der bisherigen 3x3 Matrix. Das reduziert den Rechenaufwand für jeden Punkt deutlich, kann aber zur Artefakten durch unsaubere Übergänge an den Grenzen der Zellen führen. Schau Dir die folgenden Beispiele an.
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Im Jahre 2012 präsentierte [Inigo Quilez einen interessanten Artikel über die Erzeugung präziser Voronoi-Abgrenzungen](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
Inigos Experimente zu diesem Thema hörten damit nicht auf. Im Jahr 2014 verfasste er einen schönen Beitrag über das, was er als [Voro-Noise, dt. „Voro-Rauschen“](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm) bezeichnet  eine Funktion, die einen graduellen Übergang zwischen normalem Rauschen und Voronoi-Rauschen ermöglicht. Er schrieb:
„Abgesehen von ihrer Ähnlichkeit ist es entscheidend, dass das Raster aus Zellen in beiden Mustern unterschiedlich verwendet wird. Interpoliertes Rauschen mit Zufallswerten (wie bei Value-Noise) oder mit Gradienten (wie bei Gradient-Noise) unterscheidet sich von Voronoi, wo es auf die Entfernung zum nächstgelegenen Bezugspunkt ankommt. Schließlich sind die bilineare Interpolation und die Minima-Berechnung zwei ganz unterschiedliche Operationen, nicht wahr? Doch vielleicht kann man sie in einem größeren Rahmen vereinigen? Sollte das möglich sein, könnte man sowohl Rauschmuster als auch Voronoi-Muster als Spezialfälle eines allgemeineren rasterbasierten Mustergenerators betrachten.“
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Nun ist die Zeit gekommen, dass Du Dir die Dinge genau anschaust, Dich von der Natur inspirieren lässt und Deine eigene Nutzungsmöglichkeiten dieser Techniken entdeckst!
![Deyrolle glass film - 1831](DeyrolleFilm.png)
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>

@ -0,0 +1,119 @@
![Due East over Shadequarter Mountain - Matthew Rangel (2005) ](rangel.jpg)
## Gebrochene Brownsche Bewegung
Rauschen kann für Menschen ganz unterschiedliche Dinge bedeuten, je nachdem, wen man danach fragt. Musiker denken bei diesem Stichwort an schlechte Klangqualität, Funkspezialisten an unerwünschte Überlagerungen und Astrophysiker an die kosmische Hintergrundstrahlung im Mikrowellenbereich. Diese Vorstellungen führen uns zurück zu den physikalischen Grundlagen des Zufälligen in unserer Welt.
Doch lass uns erst einmal mit etwas ganz Einfachem und Fundamentalem beginnen: Mit den Eigenschaften von Wellen. Eine Welle steht für die Schwankung einer bestimmten Eigenschaft im Verlauf der Zeit. Audiowellen sind Schwankungen des Luftdrucks, elektromagnetische Wellen sind Schwankungen elektrischer und magnetischer Felder. Zwei spezifische Merkmale von Wellen sind ihre Amplitude (die Intensität ihres Ausschlags) und ihre Frequenz (die Häufigkeit, mit der sich die Welle in einer bestimmten Zeit wiederholt). Die Formel für eine einfache lineare Welle (in einer Dimension) sieht so aus:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequenz = 1.;
y = amplitude * sin(x * frequenz);
"></div>
* Verändere in der obigen Formel doch einfach mal die Werte für die Variablen ```amplitude``` und ```frequenz``` und beobachte, was dann geschieht.
* Versuche eine formgebende Funktion in die Formel einzubringen, so dass sich die Amplitude im Verlauf der Zeit ändert.
* Versuche das Gleiche, aber diesmal für eine Änderung der Frequenz im Laufe der Zeit.
Mit den letzten beiden Übungen modulierst Du die Sinuswelle. So erhältst Du einmal eine amplitudenmodulierte Welle (AM) und einmal eine frequenzmodulierte Welle (FM).
Ein weiteres interessantes Merkmal von Wellen kann man beobachten, wenn man mehrere Wellen addiert. Dabei entsteht ein Phänomen, das als „Überlagerung“ bekannt ist. Kommentiere in dem folgenden Beispiel einzelne Zeilen aus bzw. wieder ein und verändere die verschiedenen Faktoren für die Frequenz und die Amplitude. Verschaffe Dir einen Eindruck davon, wie sich das Gesamtbild auf diese Weise ändert.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequenz = 1.;
y = sin(x * frequenz);
float t = 0.01*(-u_time*130.0);
y += sin(x*frequenz*2.1 + t)*4.5;
y += sin(x*frequenz*1.72 + t*1.121)*4.0;
y += sin(x*frequenz*2.221 + t*0.437)*5.0;
y += sin(x*frequenz*3.1122+ t*4.269)*2.5;
y *= amplitude*0.06;
"></div>
* Experimentiere mit Veränderungen der Amplitude und der Frequenz bei den hinzuaddierten Wellen.
* Gelingt es Dir, zwei Wellen zu erschaffen, die sich gegenseitig aufheben? Wie würde das aussehen?
* Ist es möglich, Wellen auf eine bestimmte Art und Weise zu addieren, so dass sie sich gegenseitig verstärken?
In der Musik steht jede Note für eine bestimmte Frequenz. Reiht man die Frequenzen der Noten einer Tonleiter aneinander, so ergibt sich ein charakteristisches Muster. Die Halbierung bzw. Verdoppelung der Frequenz entspricht dabei jeweils dem Sprung um eine Oktave nach unten bzw. oben.
Okay, lass uns die Sinuswelle nun durch Perlins Rauschfunktion ersetzen. Diese hat in ihrer einfachsten Form ein ähnliches Erscheinungsbild wie die Sinuswelle. Zwar sind die Frequenz und die Amplitude von Perlins Rauschfunktion nicht konstant, aber die Amplitude bleibt zumindest weitgehend gleich und die Frequenz schwankt nur in einem schmalen Bereich rund um die Basisfrequenz. Perlins Rauschfunktion ist dadurch nicht so gleichförmig wie eine Sinuswelle. So wird es einfacher, den Eindruck von Zufälligkeit zu erzielen, indem man mehrere dieser Funktionen erst skaliert und dann addiert. Zwar ist dies grundsätzlich auch mit Sinuswellen möglich. Doch benötigt man dann eine größere Anzahl einzelner Wellen, um den eigentlich so regelmäßigen und periodischen Charakter der zugrundeliegenden Sinuswelle zu verbergen.
Indem wir das Ergebnis mehrerer Rauschfunktionen (*oktaven*) addieren, wobei wir schrittweise die Frequenz durch Multiplikation um einen gleichbleibenden Faktor (*porositaet*) steigern und die Amplitude dieser Zuwächse verringern (*zuwachs* < 1.0), verleihen wir unserem Rauschen eine feinere Granularität mit mehr Details. Diese Technik ist als Gebrochene Brownsche Bewegung (engl.: fractal Brownian motion“), kurz *fBm*, oder auch einfach als frakturiertes Rauschen (engl.: fractal noise“) bekannt. In ihrer simpelsten Form kann man sie mit Hilfe des folgenden Codes erzeugen:
<div class="simpleFunction" data="// Properties
const int oktaven = 1;
float porositaet = 2.0;
float zuwachs = 0.5;
//
// Startwerte
float amplitude = 0.5;
float frequenz = 1.;
//
// die Oktaven durchlaufen
for (int i = 0; i < oktaven; i++) {
&#9;y += amplitude * noise(frequenz*x);
&#9;frequenz *= porositaet;
&#9;amplitude *= zuwachs;
}"></div>
* Erhöhe die Anzahl der Oktaven schrittweise von 1 auf 2, 4, 8 und 10 und beobachte, was dann geschieht.
* Sobald Du mehr als 4 Oktaven durchläufst, ändere den Wert für die Porosität.
* Ändere dann (bei *oktaven > 4*) auch den Wert für den Zuwachs, um die Auswirkungen zu beobachten.
Fällt Dir auf, dass der Funktionsgraph mit jeder zusätzlichen Oktave an Details gewinnt? Außerdem wächst die Selbstähnlichkeit: Der Kurvenverlauf in den einzelnen kleinen Abschnitten ähnelt immer mehr dem Verlauf im Großen. Dies ist ein wichtiges Merkmal mathematischer „Fraktale“ und wir simulieren dies hier durch die Oktaven-Schleife. Zwar erzeugen wir kein echtes Fraktal, weil wir die Aufsummierung nach einigen Schleifendurchläufen stoppen. Wenn wir die Schleife aber unendlich fortsetzen und immer weitere Rausch-Komponenten hinzufügen würden, erhielten wir tatsächlich ein vollständiges Fraktal.
Im Bereich der Computergrafik existieren immer Beschränkungen in der Hinsicht, wie fein wir einzelne Details auflösen können. Werden die Objekte beispielsweise kleiner als ein Pixel, macht es gar keinen Sinn mehr, weiterzurechnen. Manchmal bedarf es einer großen Anzahl an Schleifendurchläufen für hochauflösende Details, aber eine endlose Anzahl an Schleifendurchläufen und Details sind grundsätzlich nicht sinnvoll.
Der folgende Programmcode liefert ein Beispiel dafür, wie man eine fBm in zwei Dimensionen implementieren kann, um ein Muster ähnlich einem Fraktal zu erzeugen:
<div class='codeAndCanvas' data='2d-fbm.frag'></div>
* Reduziere die Anzahl der Oktaven, indem Du den Wert der Konstanten in *Zeile 37* änderst.
* Ändere die Porosität der fBm in *Zeile 47*.
* Untersuche die Auswirkungen, wenn Du die Verstärkung in *Zeile 48* veränderst.
Diese Technik wird in der Computergrafik häufig angewandt, um auf prozedurale Weise künstliche Landschaften zu erzeugen. Die Selbstähnlichkeit als Merkmal einer fBm ist perfekt für die Erzeugung von Bergen, Bergketten und ihren Tälern geeignet. Schließlich erzeugt die Erosion, die diese Strukturen in der Natur hervorbringt, ebenfalls eine Selbstähnlichkeit in mehreren unterschiedlichen Größenordnungen im Großen wie im Kleinen. Falls Dich das Thema interessiert, empfehle ich Dir diesen [hervorragenden Beitrag von Inigo Quiles über hochentwickeltes Rauschen](http://www.iquilezles.org/www/articles/morenoise/morenoise.htm).
![Blackout - Dan Holdsworth (2010)](holdsworth.jpg)
Mit mehr oder weniger derselben Technik lassen sich auch andere Effekte nachahmen, beispielsweise **Turbulenzen**. Dies geschieht mit einer fBm, wobei allerdings der Absolutwert einer vorzeichenbehafteten Rauschfunktion genutzt wird, um starke Täler zu erzeugen.
```glsl
for (int i = 0; i < OKTAVEN; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
```
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Ein weiteres Mitglied aus dieser Familie von Funktionen ist der **Bergrücken**, bei dem die tiefen Täler nach oben gekehrt werden, um scharfe Bergrücken bzw. Bergkämme zu erzeugen:
```glsl
n = abs(n); // erzeuge eine Falte
n = offset - n; // hole den Faltenboden nach oben
n = n * n; // intensiviere die Ausbuchtung
```
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Eine andere Variante dieses Verfahrens mit interessanten Ergebnissen besteht in der Multiplikation der einzelnen Rauschelemente anstelle der Addition. Man kann außerdem die Skalierung nicht gleichförmig mit jeder Detailebene (jedem Schleifendurchlauf) fortsetzen, sondern von den Ergebnissen vorheriger Schleifendurchläufe abhängig machen. Damit verlässt man allerdings die Welt klassischer Fraktale und dringt in das noch wenig erforschte Feld der „Multifraktale“ vor. Diese sind mathematisch nicht streng definiert, aber das macht sie für die Computergrafik nicht weniger nützlich. Tatsächlich werden Multifraktale bereits vielfältig in Software für die künstliche Erzeugung von Landschaftsstrukturen eingesetzt.
Falls Dich dieses Thema interessiert, kannst Du darüber z.B. in Kapitel 16 des Buches „Texturing and Modeling: a Procedural Approach“ (dritte Auflage) von Kenton Musgrave, mehr erfahren. Leider ist das Buch seit einigen Jahren vergriffen, aber man findet es noch in Bibliotheken und auf dem Gebrauchtmarkt. (Eine PDF-Version der ersten Auflage wird im Internet verkauft, aber die stammt von 1994 und enthält leider noch nicht die hier empfohlenen Kapitel.)
### Raumkrümmung
[Inigo Quiles hat einen weiteren faszinierenden Artikel](http://www.iquilezles.org/www/articles/warp/warp.htm) darüber verfasst, wie man ein fBm einsetzen kann, um einen Raum aus fBm zu verzerren. Abgefahren, nicht wahr? Das ist wie ein Traum über einen Traum, in dem es richtig rundgeht.
![ f(p) = fbm( p + fbm( p + fbm( p ) ) ) - Inigo Quiles (2002)](quiles.jpg)
Ein etwas weniger extremes Beispiel für diese Technik liefert der folgende Programmcode, wo mit Hilfe der Verzerrung wolkenartige Strukturen erzeugt werden. Auch hier spielt das Merkmal der Selbstähnlichkeit wiederum eine wichtige Rolle.
<div class='codeAndCanvas' data='clouds.frag'></div>
Die Texturkoordinaten mit Hilfe einer Rauschfunktion auf diese Weise zu verzerren, kann sehr nützlich sein und tolle Resultate liefern, es ist aber auch nicht ganz einfach zu beherrschen. Als hilfreiches Werkzeug erweist sich dabei der Ersatz der Koordinaten durch eine Ableitung (den Gradienten) der Rauschfunktion. [Ein berühmter Artikel von Ken Perlin und Fabrice Neyret mit dem Titel „flow noise“](http://evasion.imag.fr/Publications/2001/PN01/) erläutert diesen Ansatz.
Einige moderne Implementationen von Perlins Noise-Algorithmus berechnen sowohl den Funktionswert als auch den Verlauf. Wenn der konkrete Verlauf im Rahmen der prozeduralen Berechnung nicht verfügbar ist, kann man immer noch eine begrenzte Anzahl von Differenzen berechnen, um sich dem Verlauf anzunähern, auch wenn dies mehr Arbeit bedeutet und keine ganz exakten Ergebnisse liefert.

@ -1,4 +1,4 @@
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
# The Book of Shaders
*von [Patricio Gonzalez Vivo](http://patriciogonzalezvivo.com/) und [Jen Lowe](http://jenlowe.net/)*
@ -29,8 +29,8 @@ Dies ist eine behutsame Schritt-für-Schritt-Einführung in die komplexe und vie
* Generative Designs
* [Zufall](10/?lan=de)
* [Rauschen](11/?lan=de)
* Zelluläres Rauschen
* Gebrochene Brownsche Bewegung
* [Zelluläres Rauschen](12/?lan=de)
* [Gebrochene Brownsche Bewegung](13/?lan=de)
* Fraktale
* Bildverarbeitung

Loading…
Cancel
Save