Rechnen mit WGSL
WGSL bietet verschiedene Rechenoperatoren und mathematische Funktionen, mit denen wir Zahlen, Vektoren und Matrizen verarbeiten können.
Viele dieser Operationen kennen wir bereits aus unseren Beleuchtungsberechnungen. In diesem Abschnitt schauen wir uns ihre Schreibweise und ihre Ergebnisse genauer an.
Rechenoperatoren
Abschnitt betitelt „Rechenoperatoren“Die grundlegenden Rechenoperatoren sind uns aus JavaScript bekannt:
| Operator | Bedeutung | Beispiel |
|---|---|---|
+ |
Addition | a + b |
- |
Subtraktion | a - b |
* |
Multiplikation | a * b |
/ |
Division | a / b |
% |
Rest einer Division | a % b |
Wir können damit einzelne Zahlen berechnen:
let a = 10.0;let b = 4.0;
let sum = a + b; // 14.0let difference = a - b; // 6.0let product = a * b; // 40.0let quotient = a / b; // 2.5Der Operator - kann auch das Vorzeichen eines Wertes umkehren:
let direction = vec3f(1.0, 0.0, 0.0);let opposite = -direction;opposite enthält vec3f(-1.0, 0.0, 0.0) und zeigt damit in die entgegengesetzte Richtung.
Mit Vektoren rechnen
Abschnitt betitelt „Mit Vektoren rechnen“Die Rechenoperatoren funktionieren auch mit Vektoren. Bei der Addition und Subtraktion werden jeweils die entsprechenden Komponenten verrechnet.
let a = vec3f(1.0, 2.0, 3.0);let b = vec3f(4.0, 5.0, 6.0);
let sum = a + b;let difference = a - b;sum enthält vec3f(5.0, 7.0, 9.0), difference enthält vec3f(-3.0, -3.0, -3.0).
Beide Vektoren müssen dafür gleich viele Komponenten besitzen.
Vektoren multiplizieren
Abschnitt betitelt „Vektoren multiplizieren“Multiplizieren wir einen Vektor mit einer einzelnen Zahl, wird jede Komponente mit dieser Zahl multipliziert:
let color = vec3f(0.8, 0.4, 0.2);let darkerColor = color * 0.5;Das Ergebnis ist vec3f(0.4, 0.2, 0.1).
Auch zwei gleich große Vektoren können wir miteinander multiplizieren:
let a = vec3f(1.0, 2.0, 3.0);let b = vec3f(4.0, 5.0, 6.0);
let result = a * b;Dabei werden die entsprechenden Komponenten miteinander multipliziert. result enthält deshalb vec3f(4.0, 10.0, 18.0).
Das ist eine komponentenweise Multiplikation. Sie unterscheidet sich von der Multiplikation von Matrizen, bei der andere Rechenregeln gelten.
Mit Matrizen rechnen
Abschnitt betitelt „Mit Matrizen rechnen“Auch Matrizen können wir mit den Rechenoperatoren verarbeiten.
Bei der Addition und Subtraktion werden die entsprechenden Elemente zweier gleich großer Matrizen verrechnet.
Bei der Multiplikation können wir eine Matrix mit einer Zahl, einem Vektor oder einer weiteren Matrix multiplizieren.
Ein einfaches Beispiel ist die Multiplikation einer 2×2-Matrix mit einem Vektor:
let matrix = mat2x2f( vec2f(2.0, 0.0), vec2f(0.0, 3.0));
let position = vec2f(4.0, 5.0);let result = matrix * position;Das Ergebnis ist vec2f(8.0, 15.0). Die Matrix hat die erste Komponente mit 2.0 und die zweite mit 3.0 skaliert.
Die Matrixmultiplikation ist also nicht einfach eine komponentenweise Multiplikation. Wie sie mathematisch funktioniert und wie wir damit Objekte transformieren, behandeln wir später ausführlich.
Mathematische Funktionen
Abschnitt betitelt „Mathematische Funktionen“Neben den Rechenoperatoren stellt WGSL zahlreiche eingebaute Funktionen bereit.
Einige arbeiten mit einzelnen Zahlen, andere mit Vektoren. Wir können sie direkt verwenden, ohne sie selbst definieren zu müssen.
Das Skalarprodukt mit dot()
Abschnitt betitelt „Das Skalarprodukt mit dot()“Mit dot() berechnen wir das Skalarprodukt zweier Vektoren. Das Ergebnis ist eine einzelne Zahl.
let a = vec3f(1.0, 0.0, 0.0);let b = vec3f(0.0, 1.0, 0.0);
let result = dot(a, b);Das Ergebnis ist 0.0, weil die beiden Vektoren senkrecht aufeinanderstehen.
Bei zwei Vektoren gleicher Richtung mit der Länge 1.0 ergibt das Skalarprodukt dagegen 1.0.
Wir haben dot() bereits im Phong-Shader verwendet, um zu berechnen, wie stark eine Oberfläche vom Licht getroffen wird:
let diffuseFactor = max(dot(normal, lightDirection), 0.0);Das Kreuzprodukt mit cross()
Abschnitt betitelt „Das Kreuzprodukt mit cross()“Die Funktion cross() berechnet das Kreuzprodukt zweier vec3-Vektoren.
let a = vec3f(1.0, 0.0, 0.0);let b = vec3f(0.0, 1.0, 0.0);
let result = cross(a, b);Das Ergebnis ist vec3f(0.0, 0.0, 1.0). Der neue Vektor steht senkrecht auf den beiden Ausgangsvektoren.
Das Kreuzprodukt ist beispielsweise nützlich, um aus zwei Richtungen eine dazu senkrechte Richtung zu berechnen.
Vektoren normieren mit normalize()
Abschnitt betitelt „Vektoren normieren mit normalize()“Mit normalize() berechnen wir einen Vektor, der dieselbe Richtung wie der ursprüngliche Vektor besitzt, aber die Länge 1.0 hat.
let direction = vec3f(3.0, 0.0, 0.0);let normalizedDirection = normalize(direction);normalizedDirection enthält vec3f(1.0, 0.0, 0.0).
Solche normierten Vektoren verwenden wir häufig bei Beleuchtungsberechnungen, damit die Länge eines Richtungsvektors das Ergebnis nicht verfälscht.
Vektoren spiegeln mit reflect()
Abschnitt betitelt „Vektoren spiegeln mit reflect()“Die Funktion reflect() berechnet die Spiegelungsrichtung eines Vektors an einer Oberfläche.
let incoming = vec3f(0.0, 0.0, -1.0);let normal = vec3f(0.0, 0.0, 1.0);
let reflected = reflect(incoming, normal);Das Ergebnis ist vec3f(0.0, 0.0, 1.0).
reflect() kennen wir ebenfalls aus unserem Phong-Shader. Dort verwenden wir die Spiegelungsrichtung, um den Glanzlichtanteil zu berechnen.
Weitere nützliche Funktionen
Abschnitt betitelt „Weitere nützliche Funktionen“WGSL bietet viele weitere mathematische Funktionen. Einige davon werden wir regelmäßig benötigen:
| Funktion | Bedeutung |
|---|---|
abs(x) |
Absolutwert |
min(a, b) |
Kleinerer Wert |
max(a, b) |
Größerer Wert |
clamp(x, min, max) |
Wert auf einen Bereich begrenzen |
pow(x, y) |
Potenz berechnen |
sqrt(x) |
Quadratwurzel berechnen |
length(v) |
Länge eines Vektors |
distance(a, b) |
Abstand zwischen zwei Vektoren |
sin(x) |
Sinus |
cos(x) |
Kosinus |
Viele dieser Funktionen können sowohl mit einzelnen Zahlen als auch mit Vektoren arbeiten.
Beispielsweise begrenzt clamp() einen Wert auf einen bestimmten Bereich:
let brightness = clamp(1.4, 0.0, 1.0);Da 1.4 oberhalb der angegebenen Obergrenze liegt, ergibt sich 1.0.
Auch pow() haben wir bereits verwendet:
let specular = pow(specularFactor, uniforms.shininess);Mit dieser Potenzberechnung bestimmen wir im Phong-Reflexionsmodell, wie stark sich das Glanzlicht auf einen kleinen Bereich konzentriert.
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“WGSL bietet Rechenoperatoren für Zahlen, Vektoren und Matrizen. Bei Vektoren werden viele Operationen komponentenweise ausgeführt, während die Matrixmultiplikation eigenen Regeln folgt.
Zusätzlich stehen eingebaute mathematische Funktionen zur Verfügung. Besonders häufig verwenden wir in der Grafikprogrammierung dot(), cross(), normalize() und reflect().
Diese Funktionen begegnen uns bei Beleuchtungsberechnungen, Transformationen und vielen weiteren Aufgaben immer wieder.