Tutorial Lineare Algebra

Aus DGL Wiki
Version vom 22. Dezember 2005, 21:08 Uhr von MyChaOS (Diskussion | Beiträge) (Erster Teil des Tutorials)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Vorwort

Hi!

Den Meisten von euch ist sicher schon aufgefallen, dass 90% der 3D Programmierung aus Mathematik besteht. Wie willst du das nächste Doom schreiben, wenn du nicht mal feststellen kannst, ob der Spieler durch eine Wand läuft? Das problematische an der Mathematik ist, dass wenn man nicht von Natur aus eine Interesse dafür hat, erscheint sie oft etwas "trocken". Ich werde mein bestes tun, um mit Hilfe von vielen bunten Diagrammen das Ganze interessant und verständlich zu machen. Seid jedoch gewarnt, dass dies mein erstes Tutorial ist und ich mathematisch viele Sachen vielleicht etwas anders erkläre/mache als "richtig" oder optimal ist.

Ein Haufen praktischer Funktionen

Es gibt ein paar Funktionen, die man können muss, sonst kommt man nicht weit mit OpenGl. Man kann sich natürlich eine 3D Mathe Unit aus dem Internet ziehen, dies ist jedoch, meiner Meinung nach, eine schlechte Idee. Wir sind ja schließlich hier um was zu lernen. Darum lieber eine eigene kleine Unit schreiben, die man richtig versteht. Warum die Funktionen praktisch sind, erkennt man leider erst am Ende, wenn man sie in Kombination gebrauchen kann.

Vektoren

aVector := MakeVector(1,3,8);
In der Linearen Algebra geht es hauptsächlich um Vektoren. Also brauchen wir ein Vektoren Typ:
type 
  TVector = record
    X,Y,Z : Single; //Same as TGLFloat
  end;

Wir wollen die Werte nicht einzeln in unseren Vektor eintragen, sondern so:

aVector := MakeVector(1,3,8);

Also brauchen wir Folgendes:

function MakeVector(X,Y,Z : Single) : TVector;
begin
  result.x := x;
  result.y := y;
  result.z := z;
end;

Addition und Subtraktion

Datei:Beispiel.jpg

Oben sehen wir zwei Vektoren, die beide im Ursprung (gelber Punkt) anfangen. Das Resultat der Addition wird der grüne Vektor sein. Die zwei Vektoren kannst du dir als zwei separate Anweisungen denken. Die rote "Anweisung" ist gehe 2 Meter Nord; die blaue "Anweisung" ist gehe 1 Meter Ost. Wenn wir die rote "Anweisung" ausführen und die Blaue dazu addieren, d.h. auch ausführen, dann stehen wir sqr(5) Meter (Pythagoras) vom Ursprung entfernt. Das Ganze kann man auch zu einer Anweisung zusammenfassen: der grüne Vektor. Die Addition funktioniert mit alle Vektoren, nicht nur mit denen, die im 90° Winkel zueinander stehen, wie in meinem Nord-Ost Beispiel. Um die Vektoren-Summe zu erhalten, addiert man die Komponente der zwei Vektoren. Genug erklärt, hier ist die Funktion:

function AddVectors(VectorA, VectorB : TVector) : TVector;
begin
  {Simple Vector Addition.}
  Result := MakeVector(VectorA.X + VectorB.X,
                       VectorA.Y + VectorB.Y,
                       VectorA.Z + VectorB.Z);
end;  

Und das Selbe noch für die Subtraktion:

Datei:Beispiel.jpg

Das Einzige, was an diesem Diagram verwirrend sein mag, ist, dass der blaue Vektor in die "falsche" Richtung zeigt. Das liegt an dem Minus Zeichen in der Subtraktion:

Datei:Beispiel.jpg

Ein Minus Zeichen dreht einen Vektor um. Wenn die zwei Parameter der Subtraktions-Funktion vertauscht werden, dann erhält man den negativen Vektor. Das lässt sich auch mit einer einfachen Rechnung darstellen:

9 - 4 =  5
4 - 9 = -5

Hätte ich in dem Diagram oben erst den blauen Vektor angegeben wäre Folgendes rausgekommen:

Datei:Beispiel.jpg

Die Subtraktions-Funktion ist vom Aufbau her, der Additions-Funktion natürlich sehr ähnlich:

function SubVectors(VectorA, VectorB : TVector) : TVector;
begin
  {Simple Vector Subtraction.}
  Result := MakeVector(VectorA.X - VectorB.X,
                       VectorA.Y - VectorB.Y,
                       VectorA.Z - VectorB.Z);
end;

Die Länge eines Vektors

Du kennst hoffentlich den Satz des Pythagoras: r²= x² + y² Das geht auch in 3D, nur zusätzlich mit der Z-Komponente: r² = x² + y² + z²

function Magnitude(Vector : TVector) : Single;
begin
  {Returns the length of the Vector}
   result := sqrt(Vector.X * Vector.X+
                  Vector.Y * Vector.Y+
                  Vector.Z * Vector.Z);
end;

Es hat sich eingebürgert, dass man die Länge eines Vektoren auch so schreibt: |Vektor|. Um es nicht mit dem Absolut Zeichen zu verwechseln, schreiben Manche auch: ||Vektor||.

Einheitsvektoren

Einheitsvektoren sind Vektoren der Länge 1. Es ist leichter, mit Einheitsvektoren zu rechnen, als mit normalen Vektoren, da Multiplikationen mit 1 wegfallen (Siehe Skalarprodukt). Um einen Vektor der Länge 1 zu erhalten, dividieren wir die Komponente des Vektors durch die Länge des Vektors. Der Vektor ist dann "normalisiert". Die Länge des Vektors zu ermitteln ist kein Problem mehr, also können wir eine Funktion aufstellen die (fast) jeden beliebigen Vektor normalisiert:

procedure Normalize(var Normal : TVector);
var 
  Length : Single;
begin
  {The specified vector is cut to a length of 1.
  To do this each component of the vector is
  divided by the length of the vector}
  Length := Magnitude(Normal);
  if (Length = 0) then Length := 1;//We don't want a division by zero
  Normal.X := Normal.X / Length;
  Normal.Y := Normal.Y / Length;
  Normal.Z := Normal.Z / Length;
end;

Das Einzige, was hier unerwartet sein mag, ist die "if" Abfrage. Wenn der Vektor (0,0,0) ist, würde die Länge 0 sein und somit einen "Division by Zero" Fehler erzeugen. Falls die Länge 0 ist, dividieren wir deshalb durch 1.

Manchmal ist es nötig, einen Vektor um einen Faktor zu verlängern. Um dies zu erreichen, wird jede Komponente des Vektors mit dem Faktor multipliziert:

function ScaleVector(Vector : TVector; Multi : Single) : TVector;
begin
  {The Vector is scaled by the given factor}
  result.x := Vector.x * Multi;
  result.y := Vector.y * Multi;
  result.z := Vector.z * Multi;
end;

Bogenmaß und Gradmaß

Wenn du in OpenGl (und auch sonst bei PCs) irgendwo einen Winkel siehst, kannst du fast davon ausgehen, dass er im Bogenmaß (Radians) angegeben wird. Sollten es wirklich Grad sein, steht das meistens groß daneben. Die trigonometrischen Funktionen in Delphi sind übrigens auch alle auf Bogenmaß ausgelegt. Gradmaß(deg) und Bogenmaß(rad) kann man leicht umrechnen:

rad = deg / 180 * pi
deg = rad * 180 / pi

Diese Umrechnung ist hier wirklich nur als Hilfe gedacht. Rechnet bitte nicht in euren OpenGl Programmen alles nach Grad um und später wieder zurück nach Bogenmaß. Wenn du es ernst meinst, mit der 3D Programmierung, dann lern gleich mit dem Bogenmaß umzugehen.

Skalarprodukt (Punktprodukt = DotProduct)