Lektion 40
Seil Simulation
In diesem Tutorial werden wir die Simulation eines Seils behandeln. Diese Simulation basiert auf der einfachen physikalischen Simulations Engine aus Lektion 39. Um etwas aus diesem Tutorial lernen zu können, sollten Sie wissen, wie Kräft auf Massen in Simulationen angewendet werden, wie Position und Geschwindigkeit einer Masse während eines Simulationslaufs iteriert werden und wie 3D Vektor-Operationen in der Physik verwendet werden. Wenn Sie bei einem dieser Themen Zweifel haben, lesen Sie über diese noch mal in Lektion 39 und anderen Quellen nach und entwickeln Sie ein paar Applikationen.
In physikalischen Simulationen möchte man erreichen, physikalische Eigenschaften nachzubilden, die sich genauso verhalten wie in ein natürlichen Umgebung. Bewegungen in Simulationen können nicht immer exakt genauso sein wie in der Natur. Ein Modell, dass die Bewegung darstellt, die wir simulieren wollen, muss so gestaltet werden, dass sie die physikalischen Eigenschaften berücksichtigt. Das Modell, welches wir erzeugen, muss enthalten wie präzise und detailiert die Bewegung von der Simulation dargestellt werden soll. Wollen wir die Atome, Elektronen oder die Photonen darstellen und beachten oder wollen wir nur die ungefähre Bewegung eines Clusters von Partikeln darstellen? Was ist die Skalierung, die wir sehen wollen? Was ist die Skalierung für Platz und Zeit?
Die Skalierung für den Platz und die Zeit, die beobachtet werden soll, hängt ab von:
1. der mathematischen Berechnung der Bewegung
2. der Performance des Computers den wir für die Simulation verwenden
1. Mathematische Berechnung von Bewegungen:
Hier wird die Mathematik der Bewegung 'klassische Mechanik' genannt, was eine einfache Repräsentation von Massen als Partikel im Raum ist und die Beschleunigung dieser Massen durch Kräfte, während die Zeit voran schreitet. In der Skalierung, die wir mit blossem Auge sehen können, ist die klassische Mechanik durchaus zu gebrauchen. Deshalb können wir die klassische Mechanik dazu verwenden, Objekte und Mechanismen aus unserem täglichen Leben zu simulieren. In Lektion 39 wurde die Kraft der Gravitation und der Feder auf Massen von 1 Kg angewandt, indem die klassische Mechanik verwendet wurde. In diesem Tutorial werden wir die klassische Mechanik in einer Seil-Simulation verwenden.
2. Performance des Computers den wir für die Simulation verwenden:
Die Performance des Computers, auf dem die Simulation läuft, bestimmt, wie detailliert wir alles darstellen und beobachten können. Wenn wir zum Beispiel einen laufenden Menschen auf einem langsamen Rechner simulieren, würden wir darüber nachdenken, die Simulation der Zehen wegzulassen. Die Zehen haben sicherlich eine wichtige Rolle. Doch selbst ohne die Zehen in der Simulation könnten wir einen laufenden Menschen erhalten. Vielleicht wäre die Qualität der Bewegung recht niedrig, aber die Berechnungen würden weniger ins Gewicht fallen. In dem Beispiel des laufenden Menschen, zwingt uns die Performance des Computers als Massstab die Füße und Beide zu nehmen und die Zehen wegzulassen.
Die physikalischen Eigenschaften des Seils entwerfen:
Unter der Vorraussetzung, dass wir die klassische Mechanik haben (für die mathematische Berechnung der Bewegung) und einen Computer mit 500 MHz CPU Geschwindigkeit (wir wählen das als minimale Anforderung), werden wir die physikalische Eigenschaften einer Seil-Simulation entwerfen. Als erstes müssen wir bestimmen, wieviele Details wir haben wollen. Während wir den Code implementieren, werden wir Physics1.h aus Lektion 39 verwenden. In Physics1.h haben wir eine "class Mass", welche eine Masse als einen Punkt-Partikel repräsentiert. Wir können die Mass Klasse verwenden. Wenn wir diese Punkt-ähnlichen Massen mit Federn untereinander verbinden, können wir ein physikalisches Modell formen, um ein Seil darzustellen. Von dem Modell ausgehend können wir bestimmen, wie detailliert die Bewegung des Seils sein wird. Wenn wir diese Punktartigen Massen mit Federn untereinander verbinden, können wir ein physikalisches Modell formen, dass ein Seil repräsentiert. Aus dem Modell heraus können wir bestimmen, wie detailiert die Bewegung des Seils sein würde. Wir können daraus herleiten, dass das Seil Schwingende und Wellen-Bewegungen aufzeigen wird, aber keine verdrehende (swirling) Bewegung. (Stellen Sie sich dieses verdrehen so vor: sagen wir, Sie haben ein dünnes Seil zwischen zwei Fingern und Sie reiben jetzt Ihre Finger, so dass das Seil sich verdreht). Wir können das Verdrehen nicht beobachten, da wir punkt-artige Partikel in dem Modell verwenden. Punkt-artige Partikel können sich nicht um eine Achse drehen, weshalb das Seil nicht verdreht wird. Wir entscheiden uns, dass oben beschriebene Modell zu verwenden und halten fest, dass unsere Details sich auf das Schwingen und Wellen-Bewegung beschränken. Halten wir ebenso fest, dass wir die Wellen-Bewegung des Seil's im 10 cm Detail beobachten wollen. Das bedeutet, dass das Seil Diskontinuitäten unter 10 cm aufweist. Ich habe diese Einschräkung vorgenommen, da ich 50 oder 100 Partikel im Seil benutzen möchte (wegen der Performance) und ich das Seil 3 oder 4 Meter lang haben möchte. Was bedeutet, dass 3 bis 8 cm zwischen den Partikeln des Seils sind, was unter dem Diskontinuitäts-Level ist, welches wir gewählt haben (10 cm).
Die Bewegungsgleichung bestimmen:
Die Bewegungsgleichung bezeichnet im mathematischem Sinne eine Differnzial-Gleichung zweiten Grades und meint damit eigentlich, dass Kräfte in einer physikalischen Umgebung wirken. Nehmen wir die letztere Bedeutung, da sie sich beser anhört. Die Bewegungsgleichung bestimme bedeutet, die Kräfte zu bestimmen. In dem Seil-Modell werden die Kräfte auf die Partikel wirken, aus denen das Seil besteht. Die erste Kraft wird die Seil-Spannung zwischen diesen Partikeln sein. Unten wir jeder Partikel mit einem "O" gekennzeichnet und die Federn mit "----":O----O----O----O 1 2 3 4
Partikel 1 ist an 2 gebunden, 2 an 3 und 3 an 4. Wir haben 4 Partikel in diesem Seil und 3 Federn. Die Ferden sind die Ursprünge der Kräfte zwischen zwei Partikeln. Zur Erinnerung nochmal, die Federkraft ist formuliert als:
Kraft = -k * x
k: eine Konstante, die die Steifheit der Feder repräsentiert
x: Distanz der Masse zu dem Punkt, an den es gebunden ist
Die Feder-Formel, die wir verwenden werden, wird der obigen sehr ähneln. Wenn wir die obige Formel verwenden, wie sie ist, würde das Seil verwinkeln! Solange nämlich x nicht gleich null ist (x ist die Distanz zwischen zwei verbundenen Massen in unserem Seil-Modell), gibt es eine Kraft. Deshalb würden alle Partikel des Seils zueinander gezogen werden, bis x gleich null wird. Das ist aber nicht das, was wir wollen. Stellen Sie sich ein Seil vor, das auf eine Tisch gelegt wurde. Wir wollen das Seil so stetig halten, wie das Seil auf dem Tisch. Irgendwie müssen wir eine Konstante Länge ermitteln. Dafür müsste die Kraft gleich null sein, wenn x einen positiven Wert enthält. Schreiben wir die Formel dafür auf:
Kraft = -k * (x - d)
k: eine Konstante, die die Steifheit der Feder repräsentiert
x: Distanz der Masse zu dem Punkt, an den es gebunden ist
d: eine konstanter positiver Distanz-Wert, für den die Feder stetig bleibt
Mit dieser Formel wird es klar, dass, wenn die Distanz zwischen zwei Massen gleich d ist, keine Kraft angewendet wird. Sagen wir, wir haben 100 Partikel. Wenn wir d gleich 5 Zentimeter (0.05 Meter) setzen, hätten wir ein stetiges Seil von 5 Meter, wenn wir es auf einen Tisch legen. Wenn x größer als d ist, würde sich die Feder ausdehnen und wenn es weniger wäre, würde sie zusammengedrückt werden.
Nun erhalten wir mit dieser Formel die richtige Bewegung, aber es wird noch mehr benötigt. Etwas Reibung. Solange keine Reibung vorhanden ist, konserviert ein physikalisches System seine Energie. Wenn wir keinen Reibungsfaktor verwenden würden, würde das Seil nie aufhören zu schwingen. Bevor wir uns die Details zum Reibungsfaktor anschauen, werfen wir einen Blick auf den Code.
Class Spring:
Die Feder (Spring) Klasse bindet zwei Massen wendet Kräfte auf jede dieser Massen an.
class Spring // Ein Objekt um eine Feder mit innerer Reibung zu repräsentieren, welche zwei Massen verbindet. Die Feder { // hat eine normalisierte Länge (Die Länge, wo auf die Feder keine Kräfte angewandt werden) public: Mass* mass1; // Die erste Masse an einem Ende der Feder Mass* mass2; // Die zweite Masse am anderen Ende der Feder float springConstant; // Eine Konstante, um die Steifheit der Feder zu repräsentieren float springLength; // Die Länge, wo auf die Feder keine Kraft angewandt wird float frictionConstant; // Eine Konstante, die für die innere Reibung der Feder verwendet wird Spring(Mass* mass1, Mass* mass2, // Konstruktor float springConstant, float springLength, float frictionConstant) { this->springConstant = springConstant; // Setze springConstant this->springLength = springLength; // Setze die Federlänge (springLength) this->frictionConstant = frictionConstant; // Setze die frictionConstant (Reibungskonstante) this->mass1 = mass1; // Setze mass1 this->mass2 = mass2; // Setze mass2 } void solve() // solve() Methode: Die Methode wor die Kräfte angewandt werden { Vector3D springVector = mass1->pos - mass2->pos; // Vektor zwischen den zwei Massen float r = springVector.length(); // Abstand zwischen zwei Massen Vector3D force; // Force (Kraft) hat anfangs einen Wert von Null if (r != 0) // Um eine Division durch Null zu vermeiden... überprüfe, ob r gleich null ist // Die Federkraft wird zur Kraft (force) addiert force += -(springVector / r) * (r - springLength) * springConstant; ...
Im Konstruktor werden mass1, mass2 und die Konstanten gesetzt. Der betörende Teil ist die solve() Methode. In dieser Methode werden die Kräfte angewandt. Um die Kräfte anzuwenden, müssen wir die Feder-Formel verwenden, die wir erhalten haben:
force = -k * (x - d)
Ein Vektor um die Abstände zwischen zwei Massen in 3D zu repräsentieren:
Vector3D springVector = mass1->pos - mass2->pos; (Vector zwischen den zwei Massen)
wurde gefunden. Dann wird eine null Kraft erzeugt:
Vector3D force;
Dann wird die Federkraft hinzu addiert:
force += (springVector / r) * (r - springLength) * (-springConstant);
Um die obige Formel zu erhalten, müssen wir als erstes einen Einheitsvektor ermitteln, um nur den Richtungsvektor zwischen den Massen zu repräsentieren:
(springVector / r)
Dann erhalten wir, unter dem Gebrauch dieses Einheitsvektors, den (x - d) Teil der Formel in 3D mit:
(springVector / r) * (r - springLength)
Und wir multiplizieren den obigen 3D Vektor mit
(-springConstant)
was für das -k in der ursprünglichen Formel steht (das negative Zeichen bedeutet, dass mehr angezogen als abgestossen wird). Den Feder-Spannungs-Teil der Kraft haben wir fertig. Machen wir weiter mit dem Reibungs-Teil. Die Reibung findet in der Feder statt. Die Feder tendiert dazu, Energie durch diese Kraft zu verlieren. Wenn Sie eine Kraft auf eine Masse in die entgegengesetzte Richtung anwenden, in die sich die Masse bewegt, lassen Sie die Masse langsamer werden. Deshalb kann die Reibungskraft in Form von Geschwindigkeit einer Masse dargestellt werden:
Reibungskraft = -k * Geschwindigkeit
k: eine Konstante, die die vorhandene Reibung repräsentiert
velocity: Geschwindigkeit der Masse, auf die die Reibungskraft wirkt
Eine Reibungs-Formel könnte auch anders geschrieben werden, aber diese funktioniert wunderbar für unser Seil-Modell. In dieser Formel wird nur eine Masse berücksichtigt. In der Feder werden zwei berücksichtigt. Wir können die Differenz der beiden Geschwindigkeiten der Massen nehmen und die relative Geschwindigkeit daraus herleiten. Dadurch erhalten wir eine innere Reibung.
(void solve() continued) force += -(mass1->vel - mass2->vel) * frictionConstant; // Die Reibungskraft wird zu force addiert // mit dieser Addition erhalten wir die Netzkraft der Feder mass1->applyForce(force); // Force wird auf mass1 angewandt mass2->applyForce(-force); // Die Negation von Force wird auf mass2 angewandt } // Void Solve() Endet hier
Die Reibungskraft wird hier durch die relative Geschwindigkeit der Massen, die zur Kraft der Feder addiert werden, ermittlet. Die Kraft wird auf mass1 wie folgt angewandt:
mass1->applyForce(force);
und das Gegenteil der Kraft wird auf mass2 angewandt:
mass2->applyForce(-force);
In der Physik geschieht jegliche Interaktion zwischen zwei Partikeln. Eine Kraft wirkt immer auf zwei Massen in entgegengesetzte Richtung. In Simulationen kann, wenn eine Masse im Vergleich mit der anderen vernachlässigt werden kann, die Krafteinwirkung, die auf die größere Masse wirkt, vernachlässigt werden, da die Beschleunigung der größeren Masse recht klein wäre. So zieht zum Beispiel die Gravitationskraft eine kleinere Masse nach unten, und die Masse zieht die Erde etwas nach oben, wobei wir die Kraft, die auf die Erde angewandt wird, allerdings vernachlässigen.
Bisher haben wir eine Gleichung für die Bewegung aufgestellt, welche die Feder-Kräfte im Seil darstellen. Um die Simulation zu vervollständigen, sollten wir noch eine Umgebung schaffen, welche das Seil enthält und die externen Kräfte, die auf das Seil wirken, berücksichtigt. Lassen Sie uns noch ein Gravitationsfeld in diese Umgebung einfügen. Wenn es Gravitation gibt, wirken auf die Massen Gravitationskräfte. Außerdem würde ich gerne Luftreibung einfügen, was sich einfach darstellen lässt:
Reibungskraft = -k * Geschwindigkeit
k: eine Konstante, die die vorhandene Reibung repräsentiert
velocity: Geschwindigkeit der Masse, die von der Reibungskraft beeinflusst wird
Lassen Sie uns auch eine glatte Oberfläche haben, auf der wir das Seil ziehen können. Dafür müssen wir unsere Bewegungsgleichung erweitern. Gravitation, Luftreibung und die Kraft vom Boden (der glatten Oberfläche) müssen addiert werden. Die Gravitationskraft ist einfach:
force = (gravitational acceleration) * mass
Gravitation und Luftreibung werden jeden Partikel im Seil beeinflussen. Was ist mit der Kraft vom Boden? Die Kraft vom Boden wird ebenfalls auf jede Masse wirken. Wir sollten uns ein Modell vorstellen, dass den Boden repräsentiert - Seil Interaktion. Mein Modell ist recht einfach: der Boden stösst eine Masse nach oben und übt eine Reibungskraft aus. Die Kraft sollte auf eine Masse wirken, sobald diese den Boden berüht. Das müssen wir natürlich noch überprüfen.
Setzen der Startwerte der Simulation
Nun ist unsere Umgebung bereit zur Simulation. Die Einheiten sind Meter (für die Position), Sekunden (für die Zeit) und kg (für das Gewicht).
Um die Startwerte zu setzen, sollten wir die Orientierung des Seils definieren, bevor die Simulation beginnt und Konstanten definieren. Lassen Sie uns definieren, dass die Gravitation mit 9.81 m/s/s in die negative Y-Richtung wirkt. Lassen Sie uns ein Seil 4 Meter langes Seil mit 80 Partikeln platzieren. Wir wollen diese Seil horizontal haben, bevor die Simulation startet. Dafür platzieren wir jeden Partikel mit einem Abstand von 5 cm zu seinem Nachbarn (4 Meter / 80 = 0.05 Meter = 5 cm). Lassen Sie uns definieren, dass eine normale Federlänge (die Länge, wo eine Feder keine Kraft ausübt), 5 cm ist, so dass das Seil am Anfang ohne Spannung in der Simulation startet. Lassen Sie uns auch definieren, dass die gesamte Masse des Seils 4 kg ist (was ein recht schwere Seil ist). Dadurch ist jede Masse 0.05 (50 Gramm) schwer. Bevor wir weiter machen, schauen wir uns an, was wir bereits haben:
1. Gravitationsbeschleunigung: 9.81 m/s/s in negative y Richtung
2. Anzahl der Massen: 80
3. Normal Distanz zwischen zwei benachbarten Massen: 5 cm (0.05 Meter)
4. Gewicht einer Masse: 50 Gramm (0.05 kg)
5. Orientierung des Seils: horizontal platziert, ohne Spannung
Als nächstes könnten wir die Federkonstante finden. Wenn wir das Seil an einem Ende aufhängen, würde es sich sicherlich strecken. Die Feder am oberen Ende würde dabei am meisten gestreckt werden. Ich möchte nicht, dass diese Feder sich mehr als 1 cm (0.01 m) streckt. Das Gewicht, dass diese Feder trägt, ist fast das gesamte Seil (der Partikel am oberen Ende wird ausgenommen). Die Kraft ist:
f = (Masse des Seils) * (Gravitationsbeschleunigung) = (4 kg) * (9.81) ~= 40 N
Die Federkraft sollte 40 N ausgleichen:
Federkraft = -k * x = -k * 0.01 Meter
Die Summe dieser Kräfte sollte null sein:
40 N + (-k * 0.01 Meter) = 0
Von hier aus, können wir k als folgendes ermitteln:
k = 4000 N / m
Um sich einfacher zu erinnern, nehmen wir an, k sei 10000 N / m, was ein steiferes Seil ergibt, mit ungefähr 4 mm Streckung an der obersten Feder.
Um die Reibungskonstante in den Federn herauszufinden, sollten wir etwas komplizierter als oben rechnen. Deshalb werde ich den Wert benutzen, den ich durch probieren herausgefunden haben. Dieser ist:
springFrictionConstant = 0.2 N/(m/s)
0.2 N/(m/s) springFrictionConstant (FederReibungsKonstante) ist ok, um unser Seil realistisch aussehen zu lassen (das ist meine Meinung, nachdem ich die Simulation gesehen habe).
Bevor wir mit der Luftreibung und den Kräften vom Boden weitermachen, schauen wir erst einmal auf die RopeSimulation Klasse. Diese Klasse ist von "class Simulation" aus Physics1.h abgeleitet, welche in Lektion 39 erklärt wurde. class Simulation hat vier Methoden, um eine Simulation laufen zu lassen. Diese sind:
1. virtual void init() | -> | Resettet die Kräfte. |
2. virtual void solve() | -> | Die gewünschten Kräfte werden angewendet. |
3. virtual void simulate(float dt) | -> | Position und Geschwindigkeit werden iteriert. |
4. virtual void operate(float dt) | -> | Methode 1., 2., und 3. werden in eine gepackt, so dass sie alle aufgerufen werden. |
In der RopeSimulation Klasse werden wir solve() und simulate(float dt) überschreiben, da wir eine spezielle Implementation für das Seil haben. Wir werden Kräfte in der solve() Methode anwenden und eine Spitze des Seils in der simulate(float dt) Methode stabilisieren.
class RopeSimulation wird von der Klasse Simulation (aus Physics1.h) abgeleitet. Sie simuliert ein Seil mit Punktartigen Partikel, die durch Federn verbunden sind. Die Federn werden innere Reibung und eine normalisierte Länge haben. Ein Ende des Seils wird an einem Punkt im Raum namens "Vector3D ropeConnectionPos" stabilisiert. Dieser Punkt kann extern durch die Methode "void setRopeConnectionVel(Vector3D ropeConnectionVel)" bewegt werden. RopeSimulation erzeugt Luftreibung und eine glatte Oberfläche (oder Grund) mit einem Normalenvektor in die positive Y Richtung. RopeSimulation implementiert die Kraft, die auf diese Fläche angewandt wird. Im Code wird die Fläche als "ground" (Grund) bezeichnet.
Die RopeSimulation Klasse fängt wie folgt an:
class RopeSimulation : public Simulation // Ein Objekt um ein Seil zu simulieren, dass mit einer glatten Oberfläche und der Luft interagiert { public: Spring** springs; // Federn verbinden die Massen (davon sollte es [numOfMasses - 1] geben) Vector3D gravitation; // Gravitationsbeschleunigung (Gravitation wird auf alle Massen angewandt) Vector3D ropeConnectionPos; // Ein Punkt im Raum, der dazu verwendet wird, um die erste Position der // ersten Masse im System (Masse mit dem Index 0) zu setzen Vector3D ropeConnectionVel; // Eine Variable um ropeConnectionPos zu bewegen (dadurch können wir das Seil schwingen) float groundRepulsionConstant; // Eine Konstante die repräsentiert wieviel die Massen vom Boden abprallen float groundFrictionConstant; // Eine Konstante für die Reibung, die auf die Massen am Boden angewandt werden // (wird für's Schlittern des Seiles auf dem Boden verwendet) float groundAbsorptionConstant; // Eine Konstante für die Reibungs-Absorption, die auf die Massen auf dem Boden angewandt wird // (wird für vertikale Kollisionen des Seils mit dem Boden verwendet) float groundHeight; // Ein Wert der den Y-Wert auf dem Boden repräsentiert // (Der Boden ist eine glatte Oberfläche in die +Y Richtung zeigt) float airFrictionConstant; // Eine Konstante für die Luftreibung, die auf die Massen angewandt werden
Und die Klasse besitzt einen Konstruktor, dem 11 Parameter übergeben werden:
RopeSimulation( // Ein langer langerKonstruktur mit 11 Parameter fängt hier an int numOfMasses, // 1. Die Anzahl der Massen float m, // 2. Gewicht jeder Masse float springConstant, // 3. Wie steif die Federn sind float springLength, // 4. Die Länge, wo eine Feder keiner Kraft ausgesetzt ist float springFrictionConstant, // 5. Innere Reibungskonstante der Feder Vector3D gravitation, // 6. Gravitationsbeschleunigung float airFrictionConstant, // 7. Luftreibungskonstante float groundRepulsionConstant, // 8. Boden-Abstoss-Konstante float groundFrictionConstant, // 9. Bodenreibungskonstante float groundAbsorptionConstant, // 10. Boden-Absorptions-Konstante float groundHeight // 11. Höhe des Bodens (Y Position) ) : Simulation(numOfMasses, m) // Die Super Klasse erzeugt Massen mit jeweils einem Gewicht von m { this->gravitation = gravitation; this->airFrictionConstant = airFrictionConstant; this->groundFrictionConstant = groundFrictionConstant; this->groundRepulsionConstant = groundRepulsionConstant; this->groundAbsorptionConstant = groundAbsorptionConstant; this->groundHeight = groundHeight; for (int a = 0; a < numOfMasses; ++a) // setze die Anfangspositionen der Massen, iteriere mit For(;;) { masses[a]->pos.x = a * springLength; // Setze X-Position der Masse[a] mit der Distanz springLength zu seinem Nachbarn masses[a]->pos.y = 0; // Setze Y-Position auf 0, so dass sie horizontal steht masses[a]->pos.z = 0; // Setze Z-Position auf 0, so dass es einfach aussieht } springs = new Spring*[numOfMasses - 1]; // erzeuge [numOfMasses - 1] Zeiger für die Federn // ([numOfMasses - 1] Federn werden für numOfMasses Massen benötigt) for (a = 0; a < numOfMasses - 1; ++a) // um jede zu erzeugen, durchlaufe eine Schleife { // erzeuge die Feder mit Index "a" zwischen der Masse mit Index "a" und einer anderen Masse mit Index "a + 1". springs[a] = new Spring(masses[a], masses[a + 1], springConstant, springLength, springFrictionConstant); } }
[numOfMasses - 1] Federn werden erzeugt (denken Sie an die Abbildung: O----O----O----O). Die Massen werden anfangs in einer horizontalen Orientierung platziert. Wenn die angewandten Kräfte in der solve-Methode implementiert sind, wird die Bewegungsgleichung darin aufgelöst, während die Simulation läuft. Die solve Methode sieht wie folgt aus:
void solve() // solve() wird überschrieben, da wir Kräfte anzuwenden haben { for (int a = 0; a < numOfMasses - 1; ++a) // Wende Kraft auf alle Federn an { springs[a]->solve(); // Feder mit dem Index "a" soll seine Kraft anwenden } for (a = 0; a < numOfMasses; ++a) // Starte eine Schleife, um die Kräfte anzuwenden, die für alle Massen gleich sind { masses[a]->applyForce(gravitation * masses[a]->m); // Die Gravitationskraft // Die Luftreibung masses[a]->applyForce(-masses[a]->vel * airFrictionConstant); if (masses[a]->pos.y < groundHeight) // Kräfte vom Boden werden angewandt, wenn eine Masse mit dem Boden kollidiert { Vector3D v; // Ein temporärer Vector3D v = masses[a]->vel; // ermittle die Geschwindigkeit v.y = 0; // Lasse die Geschwindigkeitskomponente in die Y-Richgung aus // Die Geschwindigkeit in die Y-Richtung wird ausgelassen, da wir eine Reibungskraft erzeugen werden, um // einen Schlitter-Effekt zu erzeugen. Das Schlittern findet parallel zum Boden statt. Geschwindigkeit in die Y-Richtung wird für den // Absorption Effekt genutzt. // Bodenreibungskraft wird angewandt masses[a]->applyForce(-v * groundFrictionConstant); v = masses[a]->vel; // ermittle die Geschwindigkeit v.x = 0; // Lasse die X und Z Komponenten in der Geschwindigkeit aus v.z = 0; // Wir werden v im Absorption Effekt nutzen // Oben haben wir eine Geschwindigkeit ermittelt, die vertikal zum Boden ist und die in der // Absorptionskraft verwendet wird if (v.y < 0) // Absorbiere Energie nur dann, wenn eine Masse mit dem Boden kollidiert // Die Absorptionskraft wird angewandt masses[a]->applyForce(-v * groundAbsorptionConstant); // Der Boden sollte die Masse wie eine Feder abstossen. // Mit "Vector3D(0, groundRepulsionConstant, 0)" erzeugen wir einen Vektor in die Ebenen-Normalen-Richtung // mit einer Länge von groundRepulsionConstant. // mit (groundHeight - masses[a]->pos.y) stossen wir eine Masse so doll ab, wie sie auf dem Boden aufgekommen ist. Vector3D force = Vector3D(0, groundRepulsionConstant, 0) * (groundHeight - masses[a]->pos.y); masses[a]->applyForce(force); // Die Boden-Abstossungs-Kraft wird angewandt } } }
Im obigen Code werden erst die Federn berechnet (die Reihenfolge spielt dabei keine Rolle). Dann werden die Kräfte, die für alle Massen gleich sind, in einer for(;;) Schleife, berechnet. Diese Kräfte sind Gravitation, Luftreibung und die Kräfte vom Boden. Die Bodenkräfte sehen etwas kompliziert aus, ist aber eigentlich genauso einfach wie die anderen. Der Seil-Schlitter-Effekt auf dem Boden entsteht durch die Reibungskraft, welche die Geschwindigkeit in Y-Richtung verhindert. Y ist die Richtung, in die der Boden zeigt. Ein Schlitter-Effekt sollte nicht in die Richtung der Seite sein. Deshalb wird die Y-Richtung ausgelassen. Das ist lediglich das Gegenteil des Absorptions Effektes. Die Absoptionskraft wird nur in die Richtung der Seite des Bodens angewandt. Es gibt einen Ausnahmefall für den Absorptionseffekt: er übt keine Kraft aus, wenn eine Masse sich vom Boden wegbewegt. Ansonsten würde das Seil am Boden bleiben, während wir es hochziehen. Wir implementieren diesen Ausnahmefall mit "if (v.y <0)". Zu letzt gibt es noch die abstossende Kraft vom Boden. Der Boden stösst die Massen genauso ab, wie eine Feder, die eine Masse nach oben zieht.
Die RopeSimulation Klasse simuliert die Partikel am Anfangsindex des Seils. Der Grund dafür ist, ein Medium zu erzeugen, dass das Seil an einem Ende schwingt. Die Masse mit dem Index "0" wird seperat mittels der ropeConnectionPos und ropeConnectionVel Werte simuliert.
void simulate(float dt) // simulate(float dt) wird überschrieben, da wir simulieren wollen // Die Bewegung von ropeConnectionPos { Simulation::simulate(dt); // Die Super Klasse soll die Massen simulieren ropeConnectionPos += ropeConnectionVel * dt; // Iteriere die Positon von ropeConnectionPos if (ropeConnectionPos.y < groundHeight) // ropeConnectionPos soll nicht unter den Boden gehen { ropeConnectionPos.y = groundHeight; ropeConnectionVel.y = 0; } masses[0]->pos = ropeConnectionPos; // Die Masse mit dem Index "0" sollte an Postion ropeConnectionPos stehen masses[0]->vel = ropeConnectionVel; // Die Geschwindigkeit der Masse wird gleich ropeConnectionVel gesetzt }
Wir setzen den Wert ropeConnectionVel durch eine Methode:
void setRopeConnectionVel(Vector3D ropeConnectionVel) // Die Methode um ropeConnectionVel einen Wert zuzuweisen { this->ropeConnectionVel = ropeConnectionVel; }
ropeConnectionVel wird in der Simulation verwendet. Indem wir die Tasten verwenden, setzen wir ropeConnectionVel und wir können das Seil so bewegen, als ob wir es zwischen den Fingerspitzen halten würden.
Es gibt einige Konstanten, die nich so leicht zu bestimmen sind, bevor man die Simulation hat laufen lassen. Die Konstanten, die ich als passend empfunden habe folgen (kommen aus der Physics2Application.cpp):
RopeSimulation* ropeSimulation = new RopeSimulation( 80, // 80 Partikel (Massen) 0.05f, // Jeder Partikel hat ein Gewicht von 50 Gramm 10000.0f, // Feder Konstante in dem Seil 0.05f, // Normale Länge der Federn in dem Seil 0.2f, // Feder innere Reibungs-Konstante Vector3D(0, -9.81f, 0), // Gravitationsbeschleunigung 0.02f, // Luftreibungskonstante 100.0f, // Boden-Abstoss-Konstante 0.2f, // Boden-Gleitreibungs-Konstante 2.0f, // Boden-Apsorptions-Konstante -1.5f); // Höhe des Bodens
Indem Sie die obigen Werte ändern, können Sie verschiedene Bewegungen für das Seil erhalten. Beachten Sie, dass "Höhe des Bodes" gleich -1.5 Meter ist. Das Seil wurde bei y=0 initialisiert. Dadurch schwingt das Seil nach unten richtung Boden und kollidiert dann, was ziemlich cool aussieht. Erinnern Sie sich an Lektion 39, dass es dort einen maximalen dt Wert für eine Simualtion gab. Mit den oben gegebenen Parametern habe ich für dieses maximale dt einen Wert von 0.002 Sekunden herausgefunden. Wenn Ihre Änderungen an den Parametern das maximale dt dekrementieren, wird Ihre Simulation Instabilitäten aufweisen und das Seil wird nicht mehr richtig funktionieren. Damit es funktioniert, müssen Sie den neuen maximal möglichen Wert für dt herausfinden. Größere Kräfte und/oder kleinere Massen bedeuten mehr Instabilität, da in diesem Fall die Beschleunigung größer ist (zur Erinnerung: "Beschleunigung = Kraft / Masse").
Genauso wie in Lektion 39, wird die Simulation aus der Applikations-Datei (Physics2Application.cpp) gesteuert:
float dt = milliseconds / 1000.0f; // konvertiert Millisekunde in Sekunden float maxPossible_dt = 0.002f; // Maxmal mögliches dt ist gleich 0.002 Sekunden // Dies wird benötigt, um zu verhindern, dass ein nicht-präziser dt Wert übergeben wird int numOfIterations = (int)(dt / maxPossible_dt) + 1; // berechne Anzahl der Iterationen, die während dieser Aktualisierung gemacht werden, abhängig von maxPossible_dt und dt if (numOfIterations != 0) // verhindere Division durch null dt = dt / numOfIterations; // dt sollte entsprechend numOfIterations aktualisiert werden for (int a = 0; a < numOfIterations; ++a) // wir müssen die Simulation "numOfIterations" mal iterieren ropeSimulation->operate(dt);
Wenn Sie die Applikation laufen lassen, benutzen Sie die Pfeiltaste und 'Pos1' sowie 'Ende', um das Seil zu bewegen. Versuchen Sie ein wenig mit dem Seil zu spielen. Beobachten Sie die Wellen und Schwing-Bewegungen.
Die Simulations Prozedur geht zur Lasten der CPU. Deshalb ist es zu empfehlen, den Compiler zu optimieren. Mit den Standard 'Release' Einstellungen in Visual C++, läuft die Seil-Simulation 10 mal schneller als wie die Debug-Version. Für 'Debug' ist die Mindestanforderung an die CPU 500 Mhz. In 'Release' ist es weit aus weniger.
In diesem Tutorial wurde eine komplette Simulation präsentiert. Die physikalischen Eigenschaften, Theorie, Design und Implementierung haben Erwähnung gefunden. Fortschrittlichere Simulationen sind ähnlich der vorgestellten. Die Konzepte, die am meisten Verwendung finden, werden mit dem Seil-Beispiel abgedeckt. Das gilt sowohl für physikalische Simulationen als auch die Spieleentwicklung. Versuchen Sie Physik in Ihren Applikationen zu verwenden und kreieren Sie selbst Demos und Spiele.
Für jegliche Kommentare oder Fragen kontaktieren Sie mich bitte:
Erkin Tunca (erkintunca@icqmail.com)
Jeff Molofee (NeHe)
* DOWNLOAD Visual C++ Code für diese Lektion.
* DOWNLOAD Borland C++ Builder 6 Code für diese Lektion. ( Conversion by Le Thanh Cong )
* DOWNLOAD Code Warrior 5.3 Code für diese Lektion. ( Conversion by Scott Lupton )
* DOWNLOAD Delphi Code für diese Lektion. ( Conversion by Michal Tucek )
* DOWNLOAD Dev C++ Code für diese Lektion. ( Conversion by Warren Moore )
* DOWNLOAD Linux/SDL; Code für diese Lektion. ( Conversion by Gianni Cestari )
* DOWNLOAD Visual Studio .NET Code für diese Lektion. ( Conversion by Grant James )
Deutsche Übersetzung: Joachim Rohde
Der original Text ist hier zu finden.
Die original OpenGL Tutorials stammen von NeHe's Seite.