kind: reference search: Vektor, Punkt, Richtung, Länge, normalisieren, Skalarprodukt, dot, Kreuzprodukt, cross, Normale, reflect, Spiegeln, Strahl, lerp, mix, drehen, Winkel, atan2, raymath, vec3, Koordinaten

Vektoren

Rechnen mit Punkten und Richtungen, so wie in Raytracing, im Raycaster, mit raylib und in Shadern: was die Rechenarten bedeuten, wie man sie in den drei Schreibweisen aufschreibt, und wofür man sie braucht. Die Befehle selbst stehen auch unter raylib und GLSL.

1. Punkte, Richtungen, Farben

Ein Vektor sind zwei oder drei Zahlen. Dieselben Zahlen können verschiedenes bedeuten:

  • einen Punkt, etwa den Mittelpunkt einer Kugel oder die Position der Kamera,
  • eine Richtung mit einer Länge, etwa wohin ein Strahl läuft oder wie schnell sich etwas bewegt,
  • eine Farbe mit Rot, Grün und Blau, wie in Raytracing und in Shadern.

Im Raum zeigt x nach rechts, y nach oben und z zum Betrachter; wer geradeaus in die Szene schaut, schaut in Richtung −z. Auf dem Bildschirm und in der Karte des Raycasters zeigt y dagegen nach unten, wie die Pixelzeilen. gl_FragCoord in einem Shader zählt von unten links.

Raytracing (C) raylib (C) GLSL
Typ vec, eigenes struct Vector2, Vector3 vec2, vec3, vec4
ein Vektor (vec){1, 2, 3} (Vector3){1, 2, 3} vec3(1.0, 2.0, 3.0)
eine Komponente v.x v.x v.x, für Farben auch v.r; v.xy sind zwei auf einmal
rechnen mit Funktionen: add(a, b) mit Funktionen aus raymath.h: Vector3Add(a, b) mit Zeichen: a + b

2. Rechnen

Rechnung Bedeutung Raytracing raylib GLSL
a + b Punkt plus Richtung: der Punkt, verschoben add Vector3Add a + b
b − a Punkt minus Punkt: die Richtung von a nach b sub(b, a) Vector3Subtract(b, a) b - a
s · a streckt die Richtung auf das s-fache, negativ dreht sie um scale Vector3Scale s * a
jede Komponente mal die andere Farben mischen: rotes Licht auf einer weißen Wand mul Vector3Multiply a * b
|a| Länge length Vector3Length length(a)
a / |a| dieselbe Richtung mit Länge 1 normalize Vector3Normalize normalize(a)
|b − a| Abstand zweier Punkte length(sub(b, a)) Vector3Distance distance(a, b)
a + t · (b − a) überblenden: a bei t = 0, b bei t = 1, die Mitte bei 0,5 mix Vector3Lerp mix(a, b, t)

Die Länge kommt aus dem Satz des Pythagoras, einmal in der Ebene und einmal in die Höhe:

$$|a| = \sqrt{x^2 + y^2 + z^2} \qquad |(2, 1, -2)| = \sqrt{4 + 1 + 4} = 3$$

Normalisiert ist das \((\tfrac{2}{3}, \tfrac{1}{3}, -\tfrac{2}{3})\). Eine Richtung mit Länge 1 heißt Einheitsvektor; viele Formeln unten gelten nur für solche. Ein Vektor der Länge 0 hat keine Richtung und lässt sich nicht normalisieren: Im Raytracing wird dann durch 0 geteilt, und heraus kommt nan (»not a number«), das sich durch jede weitere Rechnung zieht; raylib gibt ihn unverändert zurück.

3. Das Skalarprodukt

Das Skalarprodukt zweier Vektoren ist eine Zahl: Komponente mal Komponente, alles zusammengezählt.

$$a \cdot b = a_x b_x + a_y b_y + a_z b_z = |a|\ |b| \cos \theta$$

\(\theta\) ist der Winkel zwischen den beiden. Für zwei Einheitsvektoren bleibt nur \(\cos \theta\), und daran sieht man, wie sie zueinander stehen:

dot(a, b) Winkel heißt
1 0° dieselbe Richtung
zwischen 0 und 1 unter 90° ungefähr in dieselbe Richtung
0 90° senkrecht zueinander
negativ über 90° eher entgegengesetzt; −1 genau entgegengesetzt

Ein Beispiel ohne Einheitsvektoren: \((2, 1, -2) \cdot (1, 2, 2) = 2 + 2 - 4 = 0\), die beiden stehen also senkrecht aufeinander. Wofür man es braucht:

  • Licht: Wie hell eine Fläche ist, hängt davon ab, wie steil das Licht auf sie fällt: dot(normal, sun_dir) für die Normale und die Richtung zur Sonne, beide mit Länge 1. Ist es negativ, liegt die Sonne hinter der Fläche, und es zählt als 0.
  • Vorne oder hinten: Das Vorzeichen von dot(dir, b − a) sagt, ob der Punkt b vor oder hinter jemandem bei a liegt, der in Richtung dir schaut.
  • Ein Stück entlang einer Richtung: Mit n der Länge 1 ist dot(a, n) die Länge von a entlang n, und dot(a, n) · n dieser Teil von a als Vektor.
  • Länge zum Quadrat: dot(a, a) ist \(|a|^2\), ohne Wurzel; gut, um Abstände zu vergleichen.

Den Winkel selbst liefert acos(dot(a, b)) für Einheitsvektoren, im Bogenmaß; raylibs Vector3Angle(a, b) rechnet ihn für beliebige Längen aus.

4. Das Kreuzprodukt

Das Kreuzprodukt zweier Vektoren im Raum ist ein Vektor, der auf beiden senkrecht steht:

$$a \times b = (a_y b_z - a_z b_y,\quad a_z b_x - a_x b_z,\quad a_x b_y - a_y b_x)$$

  • Die Richtung folgt der rechten Hand: Daumen in Richtung a, Zeigefinger in Richtung b, dann zeigt der abgespreizte Mittelfinger in Richtung cross(a, b). So ist cross((1, 0, 0), (0, 1, 0)) = (0, 0, 1): x mal y ergibt z.
  • Die Reihenfolge zählt: cross(b, a) zeigt genau in die Gegenrichtung.
  • Die Länge ist \(|a|\,|b| \sin \theta\): für zwei Einheitsvektoren im rechten Winkel 1, sonst kürzer. Wer eine Richtung braucht, normalisiert danach.
  • Zeigen a und b in dieselbe oder genau die entgegengesetzte Richtung, ist das Ergebnis (0, 0, 0). Eine Kamera, die right aus ihrer Blickrichtung und »oben« (0, 1, 0) berechnet, geht deshalb kaputt, wenn sie genau nach oben oder unten schaut.

Mit dem Beispiel von oben: \((2, 1, -2) \times (1, 2, 2) = (2 + 4,\; -2 - 4,\; 4 - 1) = (6, -6, 3)\). Es hat die Länge 9 = 3 · 3, denn die beiden stehen senkrecht.

Im Raytracing: cross, in raylib Vector3CrossProduct, in GLSL cross. In 2D gibt es kein Kreuzprodukt; dort steht (−y, x) senkrecht auf (x, y), um 90° gedreht.

5. Normalen und Spiegeln

Die Normale einer Fläche ist ein Einheitsvektor, der senkrecht auf ihr steht und nach außen zeigt. Bei einem Boden ist das (0, 1, 0), bei einer Kugel mit dem Mittelpunkt c und dem Radius r an der Stelle p ist es (p − c) / r.

Eine Richtung d, die auf eine Fläche mit der Normalen n trifft, prallt so ab:

$$\text{reflect}(d, n) = d - 2\ (d \cdot n)\ n$$

(d · n) · n ist der Teil von d senkrecht zur Fläche. Einmal abgezogen, läuft der Strahl an der Fläche entlang; zweimal abgezogen, ist dieser Teil umgedreht, und der Strahl fliegt im selben Winkel wieder weg. Der Teil entlang der Fläche bleibt, wie er war. n muss die Länge 1 haben. Im Raytracing: reflect, in raylib Vector3Reflect(d, n), in GLSL reflect(d, n).

6. Strahlen

Ein Strahl beginnt an einem Punkt origin und läuft in die Richtung dir. Jeder Punkt auf ihm ist

$$p(t) = \text{origin} + t \cdot \text{dir}$$

Hat dir die Länge 1, ist t die Entfernung vom Startpunkt: Mit origin = (0, 1, 0) und dir = (0, 0, -1) liegt p(3) = (0, 1, −3) drei Einheiten weiter. Ein negatives t liegt hinter dem Startpunkt und zählt bei einem Treffer nicht. Zu fragen, wo ein Strahl eine Kugel oder eine Ebene trifft, heißt: das kleinste positive t suchen, bei dem p(t) auf ihr liegt.

Im Raycaster setzt sich die Richtung jedes Strahls aus zwei Vektoren zusammen, der Blickrichtung und einem Stück der Kamera-Ebene quer dazu: dir + plane · camera_x.

7. Winkel und Drehen in 2D

Was Wie
die Richtung zum Winkel α, Länge 1 (cos(α), sin(α))
der Winkel einer Richtung atan2(y, x), von −π bis π; die Reihenfolge ist y, x
(x, y) um α gedreht (x · cos(α) − y · sin(α), x · sin(α) + y · cos(α)), in raylib Vector2Rotate(v, α)
um 90° gedreht (−y, x), in die andere Richtung (y, −x)

Winkel stehen im Bogenmaß: Eine ganze Umdrehung ist \(2\pi\), 90° sind \(\pi / 2\), und aus Grad wird Bogenmaß mal \(\pi / 180\). Ein wachsender Winkel dreht gegen den Uhrzeigersinn, wenn y nach oben zeigt, und im Uhrzeigersinn, wenn y wie auf dem Bildschirm nach unten zeigt.

8. Zum Nachschlagen