Tutorial Lineare Algebra: Unterschied zwischen den Versionen
Flash (Diskussion | Beiträge) K (→Nachwort) |
DGLBot (Diskussion | Beiträge) K (Der Ausdruck ''<pascal>(.*?)</pascal>'' wurde ersetzt mit ''<source lang="pascal">$1</source>''.) |
||
Zeile 11: | Zeile 11: | ||
In der Linearen Algebra geht es hauptsächlich um Vektoren. Also brauchen wir ein Vektoren Typ: | In der Linearen Algebra geht es hauptsächlich um Vektoren. Also brauchen wir ein Vektoren Typ: | ||
− | <pascal> | + | <source lang="pascal"> |
type | type | ||
TVector = record | TVector = record | ||
X,Y,Z : Single; //Same as TGLFloat | X,Y,Z : Single; //Same as TGLFloat | ||
end; | end; | ||
− | </ | + | </source> |
Wir wollen die Werte nicht einzeln in unseren Vektor eintragen, sondern so: | Wir wollen die Werte nicht einzeln in unseren Vektor eintragen, sondern so: | ||
− | <pascal> | + | <source lang="pascal"> |
aVector := MakeVector(1,3,8); | aVector := MakeVector(1,3,8); | ||
− | </ | + | </source> |
Also brauchen wir Folgendes: | Also brauchen wir Folgendes: | ||
− | <pascal> | + | <source lang="pascal"> |
function MakeVector(X,Y,Z : Single) : TVector; | function MakeVector(X,Y,Z : Single) : TVector; | ||
begin | begin | ||
Zeile 29: | Zeile 29: | ||
result.z := z; | result.z := z; | ||
end; | end; | ||
− | </ | + | </source> |
===Addition und Subtraktion=== | ===Addition und Subtraktion=== | ||
Zeile 35: | Zeile 35: | ||
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: | 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: | ||
− | <pascal> | + | <source lang="pascal"> |
function AddVectors(VectorA, VectorB : TVector) : TVector; | function AddVectors(VectorA, VectorB : TVector) : TVector; | ||
begin | begin | ||
Zeile 43: | Zeile 43: | ||
VectorA.Z + VectorB.Z); | VectorA.Z + VectorB.Z); | ||
end; | end; | ||
− | </ | + | </source> |
Und das Selbe noch für die Subtraktion: | Und das Selbe noch für die Subtraktion: | ||
Zeile 54: | Zeile 54: | ||
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: | 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: | ||
− | <pascal> | + | <source lang="pascal"> |
9 - 4 = 5 | 9 - 4 = 5 | ||
4 - 9 = -5 | 4 - 9 = -5 | ||
− | </ | + | </source> |
Hätte ich in dem Diagram oben erst den blauen Vektor angegeben wäre Folgendes rausgekommen: | Hätte ich in dem Diagram oben erst den blauen Vektor angegeben wäre Folgendes rausgekommen: | ||
Zeile 63: | Zeile 63: | ||
Die Subtraktions-Funktion ist vom Aufbau her, der Additions-Funktion natürlich sehr ähnlich: | Die Subtraktions-Funktion ist vom Aufbau her, der Additions-Funktion natürlich sehr ähnlich: | ||
− | <pascal> | + | <source lang="pascal"> |
function SubVectors(VectorA, VectorB : TVector) : TVector; | function SubVectors(VectorA, VectorB : TVector) : TVector; | ||
begin | begin | ||
Zeile 71: | Zeile 71: | ||
VectorA.Z - VectorB.Z); | VectorA.Z - VectorB.Z); | ||
end; | end; | ||
− | </ | + | </source> |
===Die Länge eines Vektors=== | ===Die Länge eines Vektors=== | ||
Zeile 82: | Zeile 82: | ||
[[Bild:Tutorial_Lineare_Algebra_Pythagoras_3d.png]] | [[Bild:Tutorial_Lineare_Algebra_Pythagoras_3d.png]] | ||
− | <pascal> | + | <source lang="pascal"> |
function Magnitude(Vector : TVector) : Single; | function Magnitude(Vector : TVector) : Single; | ||
begin | begin | ||
Zeile 90: | Zeile 90: | ||
Vector.Z * Vector.Z); | Vector.Z * Vector.Z); | ||
end; | end; | ||
− | </ | + | </source> |
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'''||. | 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'''||. | ||
Zeile 96: | Zeile 96: | ||
===Einheitsvektoren=== | ===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: | 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: | ||
− | <pascal> | + | <source lang="pascal"> |
procedure Normalize(var Normal : TVector); | procedure Normalize(var Normal : TVector); | ||
var | var | ||
Zeile 110: | Zeile 110: | ||
Normal.Z := Normal.Z / Length; | Normal.Z := Normal.Z / Length; | ||
end; | end; | ||
− | </ | + | </source> |
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. | 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: | 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: | ||
− | <pascal> | + | <source lang="pascal"> |
function ScaleVector(Vector : TVector; Multi : Single) : TVector; | function ScaleVector(Vector : TVector; Multi : Single) : TVector; | ||
begin | begin | ||
Zeile 122: | Zeile 122: | ||
result.z := Vector.z * Multi; | result.z := Vector.z * Multi; | ||
end; | end; | ||
− | </ | + | </source> |
===Bogenmaß und Gradmaß=== | ===Bogenmaß und Gradmaß=== | ||
Zeile 164: | Zeile 164: | ||
Wir werden das Skalarprodukt so ausrechnen: | Wir werden das Skalarprodukt so ausrechnen: | ||
− | <pascal> | + | <source lang="pascal"> |
function DotProduct(VectorA,VectorB : TVector) : Single; | function DotProduct(VectorA,VectorB : TVector) : Single; | ||
begin | begin | ||
Zeile 184: | Zeile 184: | ||
VectorA.Z*VectorB.Z; | VectorA.Z*VectorB.Z; | ||
end; | end; | ||
− | </ | + | </source> |
===Kreuzprodukt (Vektorprodukt = CrossProduct)=== | ===Kreuzprodukt (Vektorprodukt = CrossProduct)=== | ||
Zeile 194: | Zeile 194: | ||
Ich habe hier das Kreuzprodukt des blauen und pinken Vektoren gebildet. Die Länge des Vektors, den man durch das Kreuzprodukt erhält, ist nicht immer die selbe. Darum wird das Resultat des Kreuzprodukts meistens danach normalisiert. | Ich habe hier das Kreuzprodukt des blauen und pinken Vektoren gebildet. Die Länge des Vektors, den man durch das Kreuzprodukt erhält, ist nicht immer die selbe. Darum wird das Resultat des Kreuzprodukts meistens danach normalisiert. | ||
− | <pascal> | + | <source lang="pascal"> |
function CrossProduct(VectorA,VectorB : TVector) : TVector; | function CrossProduct(VectorA,VectorB : TVector) : TVector; | ||
begin | begin | ||
Zeile 203: | Zeile 203: | ||
Result.Z := VectorA.X*VectorB.Y - VectorA.Y*VectorB.X; | Result.Z := VectorA.X*VectorB.Y - VectorA.Y*VectorB.X; | ||
end; | end; | ||
− | </ | + | </source> |
Die Reihenfolge in der man die Vektoren der Kreuzprodukt Funktion überreicht ist wichtig. Wenn ich in dem vorigen Diagram erst den pinken Vektor überreicht hätte wäre Folgendes rausgekommen: | Die Reihenfolge in der man die Vektoren der Kreuzprodukt Funktion überreicht ist wichtig. Wenn ich in dem vorigen Diagram erst den pinken Vektor überreicht hätte wäre Folgendes rausgekommen: | ||
Zeile 220: | Zeile 220: | ||
Zum Abschluss will euch eine Funktion zeigen, die die Normale eines beliebigen Dreiecks finden kann. Dazu müssen wir erst Dreiecke speichern können: | Zum Abschluss will euch eine Funktion zeigen, die die Normale eines beliebigen Dreiecks finden kann. Dazu müssen wir erst Dreiecke speichern können: | ||
− | <pascal> | + | <source lang="pascal"> |
type | type | ||
TTriangle = array[0..2] of TVector; | TTriangle = array[0..2] of TVector; | ||
− | </ | + | </source> |
Um die Normale zu finden benötigen wir zwei Vektoren. Wir haben nur die drei Punkte des Dreiecks. Durch Subtraktion kann man aus den drei Punkten zwei Vektoren bilden: | Um die Normale zu finden benötigen wir zwei Vektoren. Wir haben nur die drei Punkte des Dreiecks. Durch Subtraktion kann man aus den drei Punkten zwei Vektoren bilden: | ||
Zeile 231: | Zeile 231: | ||
Ich hoffe das du den Zusammenhang zwischen den zwei roten bzw. blauen Vektoren sehen kannst. Wenn nicht schau dir die Vektoren Subtraktion bitte noch mal genau an. Wir gebrauchen das Kreuzprodukt um einen Vektor zu finden der senkrecht auf diesen zweien steht. Zum Schluss wird dieser normalisiert. | Ich hoffe das du den Zusammenhang zwischen den zwei roten bzw. blauen Vektoren sehen kannst. Wenn nicht schau dir die Vektoren Subtraktion bitte noch mal genau an. Wir gebrauchen das Kreuzprodukt um einen Vektor zu finden der senkrecht auf diesen zweien steht. Zum Schluss wird dieser normalisiert. | ||
− | <pascal> | + | <source lang="pascal"> |
function GetNormal(Triangle : TTriangle) : TVector; | function GetNormal(Triangle : TTriangle) : TVector; | ||
var | var | ||
Zeile 250: | Zeile 250: | ||
result := Normal; | result := Normal; | ||
end; | end; | ||
− | </ | + | </source> |
Wenn alles glatt läuft, haben wir die Normale des Dreiecks. | Wenn alles glatt läuft, haben wir die Normale des Dreiecks. |
Version vom 10. März 2009, 19:13 Uhr
Inhaltsverzeichnis
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
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
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:
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:
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:
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:
Das geht auch in 3D, nur zusätzlich mit der Z-Komponente:
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:
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)
Das Skalarprodukt ist eine der meistgebrauchten Funktionen - und das aus gutem Grund: Wenn es irgendwie um einen Winkel in einer Berechnung geht, ist wahrscheinlich das Skalarprodukt mit drin.
Diese Formel ist noch nicht brauchbar, da zwei der Variablen unbekannt sind (Das Skalarprodukt und der Winkel, im weiteren ß genannt). Bemerkenswert ist, dass wenn die Vektoren beide Einheitsvektoren sind, dann ist das Skalarprodukt gleich dem Kosinus des Zwischenwinkels. Das Problem mit den zwei unbekannten Variablen hat zum Glück Jemand für uns gelöst:
Wenn wir die zwei Gleichungen gleichsetzen, erhalten wir Folgendes:
Sehr schön. Damit können wir etwas anfangen: Wir können mit ein paar Rechnungen an den Winkel zwischen den zwei Vektoren kommen.
Aus Performance Gründen sollte dies möglichst vermieden werden, da die ArcCos Funktion langsam ist. Auch um dies Problem gibt es (teilweise) eine schlaue Lösung. Lassen wir das mit dem ArcCos weg und schauen uns den Kosinus Graphen an:
Falls der Winkel Pi/2 oder 3Pi/2 ist, wird der Kosinus 0 sein. Andersrum gedacht: Wenn der Kosinus 0 ist dann ist der Winkel Pi/2 oder 3Pi/2. Wenn Cos(ß) zwischen Pi/2 und 3Pi/2 liegt, ist das Vorzeichen von Cos(ß) negativ. Am Graphen solltest du auch sehen können das beim Rest das Vorzeichen positiv ist. Und das Ganze noch mal graphisch: (Der Rote Vektor ist der "Hauptvektor")
Wir werden das Skalarprodukt so ausrechnen:
function DotProduct(VectorA,VectorB : TVector) : Single;
begin
{The result of the DotProduct is the cosine of the angle
between the two vectors. If the result is exactly 0 then
the angle between the vectors is 90 degrees. If it is
positive then the angle is smaller than 90, if it is negative
the angle is greater than 90. Use Arccos to get the angle itself.
Anmerkung des Korrektors: Die Annahme, dass das Punktprodukt
zweier Vektoren dem Kosinus des Zwischenvektors entspricht, ist
nur dann korrekt, wenn die beiden Vektoren Einheitsvektoren sind.
(siehe: Definition des Punktproduktes). Um auch ohne Einheitsvektoren
den Winkel zu erhalten, muss man das Punktprodukt durch die
Längen der Vektoren teilen.}
result := VectorA.X*VectorB.X+
VectorA.Y*VectorB.Y+
VectorA.Z*VectorB.Z;
end;
Kreuzprodukt (Vektorprodukt = CrossProduct)
Das Kreuzprodukt ist noch eine Funktion, die man kennen muss. Das Kreuzprodukt nimmt, genau wie das Skalarprodukt, zwei Vektoren entgegen. Als Resultat erhält man beim Kreuzprodukt nicht eine Zahl, sondern einen Vektor. Das Schöne an diesem Vektor ist, dass er genau senkrecht auf der Fläche steht die durch die zwei ursprünglichen Vektoren beschrieben. Dazu ein Diagram:
Ich habe hier das Kreuzprodukt des blauen und pinken Vektoren gebildet. Die Länge des Vektors, den man durch das Kreuzprodukt erhält, ist nicht immer die selbe. Darum wird das Resultat des Kreuzprodukts meistens danach normalisiert.
function CrossProduct(VectorA,VectorB : TVector) : TVector;
begin
{Creates a Vector that is perpendicular to the plane
created by the two given vectors}
Result.X := VectorA.Y*VectorB.Z - VectorA.Z*VectorB.Y;
Result.Y := VectorA.Z*VectorB.X - VectorA.X*VectorB.Z;
Result.Z := VectorA.X*VectorB.Y - VectorA.Y*VectorB.X;
end;
Die Reihenfolge in der man die Vektoren der Kreuzprodukt Funktion überreicht ist wichtig. Wenn ich in dem vorigen Diagram erst den pinken Vektor überreicht hätte wäre Folgendes rausgekommen:
Auf diesem Diagram ist genau die selbe Fläche zu sehen wie auf dem vorigen, nur aus einem anderen Blickwinkel, damit man den Vektor überhaupt sehen kann. Der Vektor, den wir zurück bekommen, hat genau die selbe Länge wie der Vorige, er zeigt jedoch in die gegengesetzte Richtung. Er ist also das Negativ des vorigen Vektors. Negative Vektoren wurde schon in "Addition und Subtraktion" besprochen.
Es gibt eine Methode, sich zu merken, in welche Richtung das Resultat zeigen wird: Wenn du deine rechte Hand ausstreckst, gilt Folgendes: Der Zeigefinger ist der erste Vektor, der Mittelfinger der Zweite und der Daumen das Resultat. Graphisch:
Das nennt man die Rechte-Hand-Regel. Ich hoffe, dass du in dem Bild eine rechte Hand erkennen kannst ;-)
Alles was normal ist
Zum Abschluss will euch eine Funktion zeigen, die die Normale eines beliebigen Dreiecks finden kann. Dazu müssen wir erst Dreiecke speichern können:
type
TTriangle = array[0..2] of TVector;
Um die Normale zu finden benötigen wir zwei Vektoren. Wir haben nur die drei Punkte des Dreiecks. Durch Subtraktion kann man aus den drei Punkten zwei Vektoren bilden:
Ich hoffe das du den Zusammenhang zwischen den zwei roten bzw. blauen Vektoren sehen kannst. Wenn nicht schau dir die Vektoren Subtraktion bitte noch mal genau an. Wir gebrauchen das Kreuzprodukt um einen Vektor zu finden der senkrecht auf diesen zweien steht. Zum Schluss wird dieser normalisiert.
function GetNormal(Triangle : TTriangle) : TVector;
var
VectorA, VectorB, Normal : TVector;
begin
{To get the normal we first need to define a plane.
To do this we use the three vertices to create two
vectors, thereby defining a plane. If we give these
two vectors to the CrossProduct function we get a
vector that is perpendicular to the plane in return.
All we then need to do is cut this vector to a length
of 1 and we have our normal.}
VectorA := SubVectors(Triangle[0],Triangle[1]);
VectorB := SubVectors(Triangle[1],Triangle[2]);
Normal := CrossProduct(VectorA,VectorB);
Normalize(Normal);
result := Normal;
end;
Wenn alles glatt läuft, haben wir die Normale des Dreiecks.
Nachwort
Ich hoffe dieses Tutorial hat dem ein oder anderen was gebracht. Ich habe auf jeden Fall eine Menge gelernt durch das Schreiben. Ist irgendwie immer im Leben so. Sobald man etwas erklären möchte, muss man das erst selber konkret durchdenken...
Vielen dank an Delphic für die sprachliche Hilfe (War sehr nötig).
|
||
Vorhergehendes Tutorial: - |
Nächstes Tutorial: Tutorial Nachsitzen |
|
Schreibt was ihr zu diesem Tutorial denkt ins Feedbackforum von DelphiGL.com. Lob, Verbesserungsvorschläge, Hinweise und Tutorialwünsche sind stets willkommen. |