Übungen GLSL und Shader

Diese Übungen dienen zum Erlernen der Grundlagen von Shadern in OpenGL. Dazu wird mithilfe einfacher Übungen die Syntax von GLSL und die Funktionsweise von GLSL-Shadern erlernt. Die Übungen bieten (absichtlich) keinen vollumfänglichen Überblick über alle Funktionen von Shadern, sondern sollen nur einen groben Einblick in die Technik gewähren.

Hinweis

Die Übungen können komplett auf der Website ShaderToy ausgeführt werden. Dies schränkt zwar den Funktionsumfang von OpenGL ein, hat aber den (nicht zu unterschätzenden) Vorteil, das keine Tools installiert werden müssen!

Übungen

Übung 1: Schwarz/Weiß

Aufgabe

Mithilfe der step()-Funktion kann man einen Wert mit einem Grenzwert vergleichen. Liegt der übergebene Wert über dem Grenzwert, gibt die Funktion 1 zurück, sonst 0. Zeichne mithilfe dieser Funktion die rechte Hälfte des Bildschirms in Weiß und die linke Hälfte in Schwarz.

Übung 2: Rechteck zeichnen

Aufgabe

Zeichne mithilfe der step()-Funktion aus der letzten Übung ein Rechteck in der Mitte des Bildschirms. Überlege dir, woraus ein Rechteck im Kontext von GLSL bestehen könnte und implementiere die entsprechende Funktion im Shader.

Bonusaufgabe: Verschiebe das Rechteck anschließend auf verschiedene Positionen des Bildschirms.

Übung 3: Funktionen und if-Statements

Aufgabe

GLSL erlaubt die Erstellung von Funktionen und die Nutzung von if-Statements, um den Code besser zu struktureren. Extrahiere die Erstellung eines Rechtecks in eine separate Funktion rectangle() und rufe diese auf. Überlege dir dabei, welche Parameter die Funktion übergeben bekommen soll und was die Funktion zurückgibt.

Bonusaufgabe: Nutze if-Statements, um das Rechteck zu erstellen (anstelle der step()-Funktion).

Übung 4: Mouse-Position nutzen

Aufgabe

Das Tool ShaderToy bietet als uniform-Input auch die aktuelle Position der Maus. Diese wird in der Variable iMouse (Typ vec4) übergeben. Die Variable liefert dabei die folgenden Werte:
mouse.xy = Mouse-Position beim letzten Mouse-Button Down
abs(mouse.zw) = Mouse-Position beim letzten Mouse-Button Click
sign(mouze.z) = Mouse-Button ist gedrückt (positiv, wenn gedrückt)
sign(mouze.w) = Mouse-Button ist geklickt (positiv, wenn geklickt)

Nutze die Mouse-Position, um das Rechteck immer dort zu zeichnen, wo die Mouse hinklickt.

Übung 5: Animation

Aufgabe

Animationen sind ein Grundpfeiler der interaktiven Computergrafik. Damit ein Shader in GLSL animiert werden kann, muss über eine Variable der "Takt" der Animation vorgegeben werden. Dazu gibt es in ShaderToy die Variable iTime, die die abgelaufene Zeit seit dem Start des Shaders anzeigt. Die Zeit lässt sich im Anzeigefenster pausieren und anzeigen (gut zum debuggen).

Lass das rote Rechteck aus Übung 4 pulsieren (wie ein Herz). Ändere auch die Geschwindigkeit des Pulses und das Ausmaß der Größenänderung.

Bonusaufgabe: Erstelle Animationen mit den verschiedenen Funktionen sin(), cos(), tan()und mod(). Was ist jeweils der Unterschied und die Auswirkung auf die Animation?

Übung 6: Mehrere Objekte

Aufgabe

Bisher haben wir nur ein Rechteck gezeichnet. Meist benötigt man in der Computergrafik aber mehrere Objekte in einer Szene. Dabei gibt es zusätzliche Hindernisse, die man beachten muss (z. B. welches Objekt liegt vor welchem anderen).

Füge zusätzlich zum Maus-gesteuerten Objekt aus Übung 5 mehrere weitere Rechtecke in einer anderen Farbe (z. B. gelb) ein. Wie kann beeinflusst werden, welches Objekt vor welchen anderen liegt? Probiert verschiedene Ansätze aus.

Übung 7: Kreise und Distancefields

Oft werden Objekte in OpenGL nicht direkt über ihre mathematische Definition gezeichnet, sondern werden anhand eines Distancefields berechnet.
Am Beispiel des Kreises wird schnell klar, wie Distancefields funktionieren:
Stell dir vor, du stehst oben auf der Spitze eines Kegels. Der horizontale Abstand zum Rand des Kegels ist 0,5 in alle Richtungen. Wenn du dich nun entscheidest, bei welcher Distanz du den Kegel "abschneiden" willst, erhältst du mehr oder weniger große Kreise.
image-distance-field.png|300

Aufgabe

Wende ein Distancefield an, um statt der Rechtecke aus Übung 5 Kreise in deiner Szene zu zeichnen.
Überlege dir dabei, wie du das relative Koordinatensystem an das Seitenverhältnis des Bildschirms anpassen kannst, damit deine Kreise rund und nicht elliptisch sind.

Bonusaufgabe: Animiere die Farben der Kreise und die Bewegung der Kreise.

Übung 8: Radar-Anzeige

Wir haben nun verschiedene Werkzeuge kennengelernt, um zweidimensionale Bilder in GLSL-Shadern zu erzeugen. Füge dein Wissen nun zusammen und baue eine komplexere Szene in GLSL nach.

Tipp: Preprocessor Directives

In GLSL gibt es die Möglichkeit Preprocessor Directives zu nutzen, um Konstanten zu definieren. Das Schlüsselwort dafür ist #define. Die Werte könnten genauso wie normale const-Konstanten in deinem GLSL-Code genutzt werden.

Aufgabe

Erstelle eine komplexe(re) 2D-Szene mit mehreren Objekten, Animation und Mouse-Position.
Vorbild für die Szene soll die (animierte und interaktive) Ansicht eines Radars sein.

Vorbild

So soll deine Animation am Ende aussehen:

Übung 9: Raymarching (3D-Objekte)

2D-Objekte haben wir jetzt schon reichlich gesehen, aber was ist mit 3D-Objekten? Unsere Szene nutzt für 3D-Objekte die "Rechte-Hand-Regel". Um dann 3D-Objekte zu zeichnen brauchen wir einen Algorithmus der die Beleuchtung simuliert. Dafür bietet sich entweder der Raytracing oder der Raymarching-Algorithmus an.

Aufgabe

Schau dir die Implementierung des Raymarching-Algorithmus in der Lösung unten an (oder implementiere ihn selbst) und zeichne anschließend eine beleuchtete Kugel in der Mitte des Bildschirms. Teste verschiedene Parameter für die Farbe der Kugel und die Beleuchtung.

Tipp!

Eine sehr gute Step-by-Step Erklärung des Algorithmus findest du hier: Link

Übung 10: Mehrere 3D-Objekte

Nun möchten wir natürlich nicht nur eine Kugel darstellen, sondern auch mehrere 3D-Objekte dargestellt können. Dazu benötigen wir eine Funktion sdScene(), die berechnet, welches Objekt gerade vorne liegt und die die verschiedenen 3D-Objekte miteinander verknüpft. Die sdScene()-Methode wird dann an allen Stellen statt der sdSphere()-Methode genutzt.

Aufgabe

Implementiere die scScene()-Methode und baue deinen Code aus Übung 9 entsprechend um.

Bonusaufgabe: Nutze den Link unten zu den Distance Functions und füge verschiedene Objekte in deine Szene ein.

Übung 11: Individuelle Farben

Bisher hat jedes unserer 3D-Objekte die selbe Farbe genutzt. Wir können die Farbe gesammelt für alle Objekte in unserer Szene anpassen, können den Objekten aber keine unterschiedlichen Farben zuweisen. Um dies zu ermöglichen, müssen wir die Distancefield-Funktionen anpassen, sodass sie nicht nur die Distanz sondern auch die gewünschte Farbe des Objekts zurückgeben.
Es gibt verschiedene Wege das umzusetzen (z. B. vec4), wir nutzen aber structs als Möglichkeit den Code sauber zu ordnen und übersichtlicher zu gestalten. Structsgibt es auch in Programmiersprachen wie C++ und sind ähnlich zu "Klassen" in anderen Programmiersprachen.

Aufgabe

Bau deinen Code aus Übung 10 so um, dass mehrere Farben für die Objekte genutzt werden können. Nutze dafür structs. Überlege dir welche Stellen deines Codes angepasst werden müssen, um die Farbwerte weiterzugeben und auszuwerten.

Bonusaufgabe: Füge einen Fußboden bzw. Untergrund unter den 3D-Objekten hinzu.

Übung 12: Rotation

Als nächstes schauen wir uns die Rotation von Objekten in unserer Szene an. Damit können wir z. B. Rechtecke oder den Ring aus den vorherigen Übungen von der Seite ansehen. Um die Auswirkungen der Rotation besser sehen zu können tauschen wir zunächst die Kugel gegen ein Rechteck mit der Funktion sdBox. Wie das geht, solltest du aus den vorherigen Übungen bereits wissen.
Rotationen bauen auf Rotationsmatrizen auf. Um diese Matrizen in GLSL darzustellen gibt es die Datentypen mat*. In unserem Fall kann die Rotationsmatrix dann über den Datentypen mat3dargestellt werden.

Aufgabe

Implementiere eine Funktion für die Rotation in x-, y- und z-Richtung und passe deine Objekt-Funktionen entsprechend an, um eine Rotationsmatrix entgegenzunehmen.

Bonusaufgabe: Wie können nun Objekte gezeichnet werden, die keinerlei Rotation/Transformation unterliegen. Implementiere eine entsprechende mat3-Konstante, die in diesem Fall genutzt werden kann.

Bonusaufgabe: Animiere die Rotation über die Variable iTime.

Übung 13: Constructive Solid Geometry (CSG)

In der Vorlesung haben wir schon die Constructive Solid Geometry (CSG) kennengelernt, deren Grundgedanke es ist, komplexe Formen als eine Kombination aus simplen Grundformen darzustellen (Kugel, Rechteck, Zylinder, ...). Grundzüge der CSG kann man auch in GLSL nachbauen. Dafür ist eine Änderung der Methode sdScene()notwendig.

Aufgabe

Baue die Methode sdScene() so um, dass ein komplexes Objekt aus verschiedenen Grundformen entsteht. Dies kann zum Beispiel ein Rechteck mit einem kreisförmigen Loch sein oder ein anderes Objekt deiner Wahl. Beispiel-Implementierungen für die Mengenoperationen findet ihr unter dem Link unten zu den Distancefunctions.

Übung 14: Kamerabewegung

Nicht in jeder 3D-Szene ist es ausreichend die Objekte zu zeichnen und zu transformieren. Manchmal ist es auch notwendig, die Kameraperspektive an sich zu ändern, um mit der Szene zu interagieren.

Aufgabe

Implementiere die Kamerabewegung, indem du die Variable ro(ray origin) anpasst. Dazu kannst du die bekannten Transformationen für 3D-Objekte nutzen (Rotation/Bewegung).

Bonusaufgabe: Implementiere die Kamerabewegung abhängig von der aktuellen Maus-Position, damit man die Kameraperspektive über Mouse-Drag-Gesten anpassen kann.

Übung 15: Texturen

In realen 3D-Anwendungen werden oft Texturen genutzt, um die Oberflächen von Objekten einzufärben. Damit lassen sich auf einfache Weise auch komplexe Oberflächen (z. B. Stoffe) darstellen. In GLSL steht dafür die Funktion texture() bereit. Über die Channel auf der unteren Seite können dann verschiedene Texturen in das Programm "geladen" werden. Das Sampling der Texturen wird dabei von ShaderToy automatisch bereitgestellt.

Aufgabe

Färbe die Objekte in deiner Szene mit Texturen ein.

Bonusaufgabe: Implementiere die Kombination aus Textur und Rotations-Animation. Dabei muss beachtet werden, dass die richtigen Textur-Koordinaten für die Rotation genutzt werden. (Tipp: Dabei kann die π und die Funktionmod()sehr hilfreich sein.)


Hilfreiche Ressourcen

Hinweis

Hier findet ihr eine Sammlung weiterer hilfreicher Links, die euch beim Erlernen von GLSL und Shadern weiterhelfen können.

Tutorials

Sonstiges

Impressum