Ich bin gerade dabei den Code meines Projektes aufzuräumen bzw. bestehendes zu erweitern, bevor ich den nächsten großen Abschnitt in Angriff nehme.
Bei der "Kamera" Klasse habe ich zb. bei den Kamerabewegungen eine Beschleunigung eingebaut. Dabei kam die Frage auf, wie ich später die Beschleunigung von Objekten, die sich zu einer bestimmten Position bewegen sollen oder von Animationen, implementieren soll.
Folgende Anforderungen habe ich mir gesetzt:
- Lineare Geschwindigkeitsinterpolation (Beschleunigung und Bremsbeschleunigung)
- Variable Beschleunigung und Maximalgeschwindigkeit.
- Völlig Zeit unabhängig (Position wird immer getroffen auch wenn weitaus mehr Zeit verstrichen ist als benötigt).
Diese Funktion ist dabei herausgekommen.
Code:
{
V0 = aktuelle Geschwindigkeit
S0 = Weg der noch zurückgelegt werden muss
A = Beschleunigung
Vmax = maximale Geschwindigkeit
Td = vergangene Zeit
}
function InterpolateS(var V0, S0:Double; A, Vmax, Td:Double):Double;
var
T, St, Sb, Sr, Vt:Double;
begin
Result :=0;
while Td > 0.00001do
begin
Sr :=0;
Sb :=(V0 * V0)/(A *2);
// Vmax begrenzen wenn diese nicht erreicht werden kann
St :=(S0 - Sb)*0.5+ Sb;
Vt :=Sqrt(2* St * A);
if Vt < Vmax then
Vmax := Vt;
// deceleration
if((Sb +0.0001) > S0)then
begin
T := V0 / A;
if(T < Td)or(S0 < 0.0001)then
begin
// Sr := V0 * Td - 0.5 * (A * (Td * Td)); // nur zum Testen
Nur bin ich mit dieser Funktion nicht wirklich zufrieden, den sie ist nicht sonderlich schnell und muss sogar noch erweitert werden, da Weg und Geschwindigkeit auch negativ sein können. Außerdem müsste diese Funktion bei einem jedem Objekt, das bewegt wird, für alle drei Achsen und die Rotation ausgeführt werden.
Ist das überhaupt der richtige Lösungsansatz oder wie wird dieses Problem zb. in Spielen gelöst?
Ich weiß nicht was du da berechnest (deine Variabelnamen sind dabei nicht gerade hilfreich), aber eigentlich muss man das Integral der Beschleunigung über die Zeit berechnen um ein physikalisch korrektes Ergebnis zu erhalten. Da Integrale nun mal nicht so leicht zu berechnen sind, verwendet man meistens eine Euler-Integration. Das heißt nichts weiter als das man die Beschleunigung für jeden Zeitschritt als konstant annimmt. Dann lässt sich die Geschwindigkeit v und die Position p eines Objektes in jedem Zeitschritt so berechnen:
Code:
v = v + (timeElapsed / m) * j;
p = p + timeElapsed * v;
Dabei ist j die Summe alle Kräfte und m die Masse des Objektes. Wenn du willst kannst du dann noch eine maximale Geschwindigkeit einbauen, wobei ich empfehle dies so zu implementieren:
Code:
w = timeElapsed * c * v * v;
if (|w| > |v|) { v = (0,0,0); }
else { v = v - w; }
Dabei ist c eine geeignete Konstante, z.B. 0.01. Bei eine Geschwindigkeit von 10 m/s würde dein Objekt so pro Sekunde um 1 m/s langsamer. Je schneller ein Objekt ist, desto schneller bremst es ab. Dadurch das v quadriert wird gibt es eine maximale Geschwindigkeit die aber nur schwer erreicht werden kann.
Zitat:
Außerdem müsste diese Funktion bei einem jedem Objekt, das bewegt wird, für alle drei Achsen und die Rotation ausgeführt werden.
In meinem obigen "Code" sind j,v und p natürlich 3D-Vektoren.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Im Prinzip hat Coolcat das meiste schon genannt. Üblich sind ein Richtungsvektor, Positionsvektor und DeltaTime Variable.
Um In Kamera Richtung zu Bewegen wird einfach Position=Position+(Richtungsvektor*DeltaTime*Geschwindigkeit) gerechnet.
Um Seitwärts zur Richtung zu bewegen, also Strafe, berechnet man das Kreuzprodukt aus UpVektor(üblich x=0,y=1,z=0) und RichtungsVektor und der resultierende Vektor wird wieder mit Position=Position+(neue Vektor*DeltaTime*Geschwindigkeit) gerechnet.
Um entsprechende Gegenrichtungen zu erreichen kann man entweder noch ein negativen Richtungsvektor(Performancetechnisch besser) speichern oder einfach vor der Rechnung durch Vorzeichenwechsel errechnen und diesen Vektor statt Richtungsvektor zu nehmen.
Die Ausrichtung(nicht Richtungsvektor) wird oft in Quaternion oder als 3D Vektor auf sinus/cosinus Basis mit Winkel für die einzelnen Achsen erstellt. Sinus/Cosinus ist sehr Teuer, wenn man nicht gerade eine Lookuptable nutzt aber Quaternion sind recht teuer beim Umwandeln in die OpenGL geforderten RotationsMatrizen. Deswegen gibt es auch so unterschiedliche Kamerasysteme, es gibt viele Möglichkeiten für unterschiedliche Anforderungen und Komplexität.
Beschleunigung ist eine einzelne Variable, welche durch Multiplikation mit in die Formel einfließen kann, bzw. ein Vektor, wenn die Beschleunigungen auf den einzelnen Achsen unterschiedlich sein sollen. Diese Variable wird bei der Aktualisierung durch ein If konstruk unter Maximalgeschwindigkeit gehalten.
Wenn FPS drops oder peeks auftreten, dann wird die DeltaTime Variable stark verfälscht und deswegen entstehen dann falsche Positionen und so weiter.
Hierfür gibt es Kompensierungen indem, man z.B. eine Oberschranke einbaut, wird diese überschritten(zu wenig FPS), dann wird der Schrankenwert verwendet, welcher bei Physiksystemen in der Regel zwischen 25,27,30 und 60FPS(Minimale Frequenz für CRT Monitore und maximale für das Auge Wahrnehmbare Schranke für die meiste Bevölkerung) differiert. Also 1Sekunde / 25Frames=0,04..Sekunden Pro Frame, dies sind also eine DeltaTime von 40Milisekunden. Die 25Frames kommen vom PAL System, welches auf einer Bildwiederholfrequenz von 25Hz also 25FPS fest gelegt ist. Das rührt von Spielekonsolen her, welche ja auf PAL oder NTSC basieren und PAL ist der Standard mit der geringsten Bildwiederholfrequenz. Also steigt beim Update von DeltaTime der Wert über 40MSek, dann wird DeltaTime auf 40MSek gesetzt.
Es gibt noch Animationspfade, welche die Position und Richtung an einem bestimmten Zeitpunkt fest legt. Hierbei würde ich empfehlen einen Pfad für Position und einen für Direction zu speichern, wobei für jeden Punkt im Pfad der Keyframe angegeben wird, woraus dann per Linearer gleichung die Position für Zeit X gefunden werden kann.
Zum Thema optimierung, da geht soviel, da schreiben Menschen Bücher drüber, drum mal die meiner Meinung nach sinnvollsten Optimierungen.
Benutzt Float statt Double, denn die GPUs nutzen noch nicht lange Floats(vorher Half-Floats) und double ist erst noch in Aussicht. Die CPU ist ebenfalls nicht für Double optimiert, die FPU ist auf 4xFloat(sind 8 ALU Einheiten, jeweils eine für Mantisse und Exponent) und Double dauern knapp über die doppelte Zeit als float. Da weder GPU noch CPU Double nutzen macht es auch wenig Sinn. Erst bei Physikalischen Berechnungen, wo solch eine starke Genauigkeit notwenidig ist macht es wirklich Sinn. Ich kann mich dunkel erinnern, das bei Jedi ich mal eine SSE/MMX optimierte Vektorbibliothek gesehen habe, sonnst würde ich mal empfehlen eine zu suchen. Die Unterschiede sind von Compiler zu Compiler leider stark unterschiedlich, da z.B. einige sehr gute FPU optimierungen machen und dann der Geschwindigkeitszuwachs bei SSE/MMX Umstellung noch vieleicht Faktor 4-5 schneller ist wärend es auf anderen gut 20x schneller wird. Vektorbibliotheken machen dann auch dein Code übersichtlicher.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Im Prinzip macht diese Funktion nichts anderes als den Weg "S0" zeit-basierend abzufahren. Es wird so weit beschleunigt wie es der aktuelle Weg oder die Maximalgeschwindigkeit zulässt, damit nach dem Abbremsen (mit negativer Beschleunigung) auf Geschwindigkeit = 0 der Weg auch 0 ist. Quasi Geschwindigkeitsgenerator und Positionierung in einem. Wobei ich mittlerweile zum Entschluss gekommen bin das, dass nicht so schlau ist, und ich es trennen werde.
Zitat:
Hierfür gibt es Kompensierungen indem, man z.B. eine Oberschranke einbaut, wird diese überschritten(zu wenig FPS), dann wird der Schrankenwert verwendet, welcher bei Physiksystemen in der Regel zwischen 25,27,30 und 60FPS(Minimale Frequenz für CRT Monitore und maximale für das Auge Wahrnehmbare Schranke für die meiste Bevölkerung) differiert. Also 1Sekunde / 25Frames=0,04..Sekunden Pro Frame, dies sind also eine DeltaTime von 40Milisekunden.
Diese Funktion hat kein Problem damit. Egal wie hoch TimeDelta (Td) ist, es würde nur der vorgegebene Weg gefahren werden.
Werde jetzt erstmal das Ganze mit Richtungsvektoren, sowie Geschwindigkeitsgenerator und Positionierung getrennt umsetzten. Wobei eine Masse bei der Kamera zur Zeit noch keine Rolle spielt.
Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.