Registriert: Sa Apr 14, 2012 14:28 Beiträge: 52
Programmiersprache: c++
Hallo zusammen,
ich hatte gerade eine recht interessante Diskussion mit einem Freund bzgl einer geplanten Weltraumsimulation. Es ging darum, welchen Datentyp man für die Weltkoordinaten verwenden sollte.
Das Problem im Weltraum ist ja, dass man es mit mit relativ großen Entfernungen zu tun hat (Abstand Sonne Saturn: 1500 *10^9 Meter). Fließkommazahlen können nur eine gewisse Anzahl an Stellen speichern und verschieben den Wertebereich in die gewünschte Region. Durch dieses "Verschieben" können sie zwar einen größeren Wertebereich mit 32bit oder 4 bit erfassen, als ein entsprechender Integer, allerdings kommt es zu Datenverlusten. Wir haben uns folgendes Beispiel überlegt:
Es seien 2 Objekte, die einen Meter Abstand voneinander haben, mit Fließkommazahlen beschrieben. Diese liegen nun in den Weltkoordinaten (wir bleiben mal eindimensional) bei 10m und 11m. Die Fließkommazahl soll bis zu 4 Stellen abbilden können. Ist in diesem fall kein Problem, denn es ginge beispielsweise: 0.110 - 0.100 = 0.010. Somit entsteht bei der Abstandsberechnung kein Fehler. Was ist aber nun, wenn wir die selben Objekte um sagen wir 10^5 Meter verschieben. Dann würde die Berechnung 0 zurück liefern, denn die ersten 4 Zahlen von 10011 und 10010 sind jeweils 1-0-0-1. Somit würden also Objekte die zu den Rändern des Systems verschoben werden in ihren Positionen immer ungenauer definiert werden können und im schlimmsten Fall ineinander fallen.
Mache ich da nun irgendeinen Denkfehler oder sind meine Ausführungen korrekt? Wenn ja, würde es heißen, dass ich lieber mit Integern der ensprechenden Größe rechne (vermutlich longint). Wir wollen schon mindestens eine Weltgröße von 10^9 km haben, gleichzeitig aber Annäherungen auf den mm (10-3m) genau berechnen können, egal wo man sich gerade befindet.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
10⁹ km = 10¹²m. Ihr wollt also einen Wertebereich von 10¹⁵. Das ist praktischerweise genau die Genauigkeit eines doubles . Aber ja, genau das Problem existiert. Deshalb teilt man die Welt z.B. in Sektoren ein, deren Position zueinander fix ist. Der Vorteil ist dann, dass die Objekte ihre Position innerhalb des Sektors mit hoher genauigkeit kennen. Über Sektorgrenzen hinweg ist die Positionsungenauigkeit nicht mehr so wichtig, weil die Objekte sowieso ewig weit voneinander entfernt sind.
An der Genauigkeitsgrenze vom Datentyp arbeiten ist nicht unbedingt das gelbe vom Ei.
grüße
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: So Aug 08, 2010 08:37 Beiträge: 460
Programmiersprache: C / C++ / Lua
Ihr könntet natürlich auch eigene Datentypen schreiben und verwenden. Nachteil wäre allerdings, dass es nicht so schön wäre diese zu verwenden und der größere Speicherverbrauch und die langsameren Berechnungen.
_________________ offizieller DGL Compliance Beauftragter Never run a changing system! (oder so)
Ich hatte mal einen TimerManager gebastelt. Da hast du quasi dasselbe Problem, da ich GENAUER sein wollte als Milisekunden. Also sollte ich doubles nehmen? Wollte ich nicht, da diese, je länger das Programm läuft, unpräziser werden.
Lösung: neue Time-Klasse mit überschriebenen Operator-Funktionen (du nutzt ja schließlich C++ ), hat eine Int für den Vorkommaanteil und einen Double für den Nachkommaanteil.
Und so könntest du es auch machen, du hast einen Int für beispielsweise Kilometer und für alles kleinere nimmst du dann den double. Oder halt die Sektorenzerlegung die Lord Horazont vorgeschlagen hat.
Registriert: Sa Apr 14, 2012 14:28 Beiträge: 52
Programmiersprache: c++
Das mit den Sektoren ist uns auch schon inden Sinn gekommen, nur würde dies ja zusätzlichen Aufwand bedeuten, da man Übergabeparameter etc definieren müßte. Weiterhin bräuchte ich zusätzlichen Speicher für die Sektoren. Nehmen wir longints, hätten wir 2^64 Werte zur Verfügung. Eine kurze Überschlagsrechnung hat gezeigt, dass wir bei einer kleinsten Werteeinteilung von 10e-6 (ein tausenstel Millimeter) immer noch eine Welt mit 12 facher Größe der Distanz von Saturn und Sonne erzeugen könnten. Ich würde mal behaupte selbst bei nem Ego-Shooter sind 0.000001m mehr Genauigkeit als man braucht.
Generell würde ja mal eine Gegenüberstellung der Vor- und Nachteile von Interger gegenüber Float interessant sein. Bisher sähe die bei mir so aus:
Zitat:
Vor und Nachteile von Integer gegenüber Float: ----------------------------------------------------------
+ exakt definiert + keinerlei operative Fehler in Addition, Subtraktion und Multiplikation - wesentlich kleinerer Wertebereich - teilweise Konversationen für bestimmte mathematische Funktinen notwendig(Wurzel ziehen beispielsweise)
Du hast aber unter Umständen vielleicht UNPRÄZISERE Divisionen als bei Fließkomma.
Ein Vorteil bei Sektor wäre das du die lokalen Koordinaten besser an OpenGL übergeben kannst. Meines Wissens nach gibt es keine Möglichkeit 64Bit-Ganzzahlen in Matrizen zu packen.
Registriert: Sa Apr 14, 2012 14:28 Beiträge: 52
Programmiersprache: c++
hm, das mit der KONVERTIERUNG (Ich und Fachwörter... ) ist die Frage. Möglicherweise schlägt das etwas auf die Geschwindigkeit. Schließlich muss ich ja alle Weltkoordinaten transformieren für OpenGL... :/ Alles nicht so einfach. Ich muss mir das echt nochmal durch den Kopf gehen lassen... Vielleicht wäre auch die angegebene eigene Variablndeklaration sinnvoll...
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Du musst dabei weiterhin beachten, dass du OpenGL nicht einfach die absoluten Koordinaten füttern kannst, denn da wird das ganze wahrscheinilch gar zu einem 32-bit Float. Selbst wenn du in deiner Anwendung hochpräzise rechnest, kannst du dann am Rande des Genauigkeitsbereiches lustige Grafikfehler bekommen.
Dementsprechend musst du das alles in einen lokalen Raum, z.B. die Kameraposition umrechnen, was nochmal aufwand bedeutet (da du dies jeden Frame machen musst, während du z.B. die Koordinaten relativ zur Sektormitte einfach für alles verwenden könntest). Die Berechnung kann nicht in OpenGL passieren, da du eben deine präzisen Werte nicht in Matrizen bekommst.
grüße
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Letztendlich wird das ganze so verlaufen: Meine Weltkoordinaten werden aufgrund der Präzision LongInts werden. Bevor OpenGL die Koordinaten überreicht bekommt, rechne ich alle Weltkoordinaten in Kamerabezugskoordinaten um (simple Subtraktion). Die werden dann noch in Float konvertiert und anschließend an OpenGL übergeben. Somit nimmt die Präzision mit dem Abstand zur Kamera und nicht vom Weltenursprung ab. Ob ein paar tausend Kilometer entfernte Objekte nicht mehr auf den Meter genau abgebildet werden können, kann mir ja egal sein, da diese meist gar nicht mehr gerendert werden.
Einzige Überlegung die noch bleibt: Wie stelle ich mein Frustrum richtig ein? Eine zu große Spanne zwichen Near und Far soll sich ja angeblich negativ auswirken, allerdings will ich die weit entfernten, riesigen Planeten auch noch rendern... einfach manuell skalieren und bis ins Frustrum vor ziehen?
Registriert: Mi Nov 30, 2011 21:41 Beiträge: 136 Wohnort: Bad Vilbel
Programmiersprache: Delphi 7
Es gilt eigentlich immer für den Far-wert: So groß wie nötig, so klein wie möglich. (So groß dass man die relevanten Sachen noch sieht und nicht einfach riesige Objekte einfach auftauchen, aber so klein wie möglich, dass deine Anwendung noch performant bleibt.) Aber hier ein Trick wegen den Planeten:
Viele Spiele verwenden für sowas Billboards. Kurz du renderst die Planeten in ein FBO und packst dann die Textur einfach auf ein Quad in die Ferne. Eine performante Umsetzung wäre:
- Colorbuffer leeren - Depthtest deaktivieren und Planenten rendern. - Depthbuffer leeren und Depthtest wieder aktivieren - Weltraumszene zeichnen - SwapBuffers()
Vorteil von dieser Reihenfolge: Es muss Color/Tiefenbuffer nur einmal geleert werden und du musst nicht aufpassen, dass deine Planeten ausversehentlich anderen Objekte verdecken.
Vorteil von Billboards: Nahezu kein Performanceverlust, da für jeden Planet nur ein Texturiertes Quad gerendert werden muss. Das rendern in die FBOs kannst du einmal am Anfang machen, denn auf die Ferne sollte es ja egal sein, von welcher Seite du gerade den Planeten betrachtest.
Nachteile von Billboards: - Schatten für die Billboards müsstest du, wenn du sie umbedingt brauchst, selbst berechnen. - Man sieht den Planeten immer von der selben Seite.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Die beste Implementierung die ich kenne ist ein 3D Array für Sektoren und double precision für ein Sektor(wenn es um genauigkeit geht). Man kann auch auf float setzten und sollte dann auf jeden Fall zwischen -1.0 und 1.0 bleiben und im vertex shader auf die echte dimension skalieren, um die Ungenauigkeit möglichst bis zum Ende zu vermeiden. Double ist so Präzise, dass man mit großen Zahlen arbeiten kann ohne Probleme mit der Genauigkeit zu bekommen aber man muss immer zwischen float und double konvertieren, weil gpu nun mal flink mit floats sind.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
@Dinge in der Ferne: Für den Fall, dass sich die fernen Objekte nicht mit nahen überlappen (was bei riesigen weit entfernten Planeten der Fall sein sollte), kann man auch mehrere Passes machen, wobei unterschiedliche Near und Far-Werte nimmt.
Einen Pass für die nahen Objekte mit möglichst kleinem Far, und einen Pass für die fernen Objekte, mit möglichst großem Near und beliebig großem Far.
grüße
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Um 2Pass verfahren zu umgehen, kann man auch Imposter verwenden. Man rendert für die fernen Planeten und Himmelskörper Quads, mit einem vorher im fbo gerendertem textur und verwendet die so lange, bis der kamerawinkel nicht mehr stimmt oder sich der Winkel des Objektes ändert und die Textur aktualisiert werden muss. Dabei wird der Tiefentest deaktiviert und von hinten nach vorne sortiert und gerendert. Ja da gibt es overdraw aber das sind Objekte, die auf dem Bild eh nur noch so 32x32 Pixel ein nehmen, wenn überhaupt.
Für alles, was mehr Platzt ein nimmt, lohnen sich Cube Imposter, also man rendert per Shader eine Kugel in den Cube und tut per raytracing die textur drauf projezieren. Alternative Raytracing auf ein Volume Textur. Das sollte aber dann bei größer werden, des Renderbereiches auch nicht mehr verwendet werden. Für Objekte die noch größer werden, switcht man dann auf Mesh um und kann dann über Chunked LOD mit mesh morphing ohne ploppen gemacht werden.
Das ganze läuft auch auf älteren Karten sehr schnell, skaliert wunderbar und man hat kein ploppen. Der Nachteil ist, man hat viel aufwand die Systeme ein zu bauen aber LOD ist nun mal ein Komplexes Thema.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Sa Apr 14, 2012 14:28 Beiträge: 52
Programmiersprache: c++
Ohje.. jetzt fliegen ja doch schon einige Fachwörter durch den Raum, die ich erstmal googeln muss... Außerdem befinde ich mich noch am Anfang von http://arcsynthesis.org/gltut/ . Ich habe zwar schon einige nette Programme mit OpenGL verzapft aber richtiges 3D habe ich bisher immer umschiffen können. Deswegen mögen man mir verzeihen wenn ich an einigen Stellen noch nicht ganz mitkomme. Ich bemühe mich das so schnell wie möglich aufzuarbeiten
Also wenn ich Lord Horazont richtig verstanden wäre der vorschlag prinzipiell folgendermaßen:
- Projektionsmatrix setzen (...,...,Near =sehr groß, Far = Noch größer) - weit entfernte Objekte rendern - Projektionsmatrix setzen (...,...,Near =normaler Wert, Far = normaler Wert) - restliche Objekte rendern - Buffer swappen
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Jupp, und zwischen Fern und Nah den Tiefenpuffer killen. Was Tak erzählt ist aber definitiv die coolere Profi-Variante.
grüße
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Mitglieder in diesem Forum: 0 Mitglieder und 3 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.