Meine OpenGL-Szene enthält Objekte, die in lächerlich großen Entfernungen vom Ursprung positioniert sind. Wenn ich diese Objekte ansehe und eine Kamera um sie schwenke, drehe oder zoome, zittern sie. Das heißt, die Scheitelpunkte, aus denen sich die Objekte zusammensetzen, scheinen um ein imaginäres 3D-Gitter von Punkten zu rasten. Ich habe gelesen, dass dies ein häufiges Problem ist, da viele Informationen mit Gleitkomma-Genauigkeit gespeichert werden können (was OpenGL und so ziemlich alles andere verwendet). Ich verstehe nicht, warum das passiert.
Bei der Suche nach einer Lösung stieß ich auf die sehr einfache Fehlerbehebung mit „schwebendem Ursprung“, und sie scheint zu funktionieren. Ich transformiere einfach alles so, dass sich meine Objekte in den gleichen relativen Positionen befinden, aber was auch immer meine Kamera betrachtet, ist nahe am Ursprung. Ich habe hier eine Erklärung gefunden: http://floatingorigin.com/ , aber ich konnte ihr nicht folgen.
Könnte jemand erklären, warum das Positionieren meiner Szene sehr weit weg (sagen wir 10 Millionen Einheiten) vom Ursprung zu dem von mir beobachteten unregelmäßigen Verhalten führt? Und warum kann das Problem behoben werden, wenn Sie es in die Nähe des Ursprungs verschieben?
Antworten:
Dies ist ALLES aufgrund der Art und Weise, wie Gleitkommazahlen in Computern dargestellt werden.
Ganzzahlen werden ziemlich einfach gespeichert. Jede Einheit ist genau "eins" von der "vorherigen", so wie Sie es bei abzählbaren Zahlen erwarten würden.
Bei Gleitkommazahlen ist dies nicht genau der Fall. Stattdessen geben mehrere Bits das EXPONENT an, und der Rest gibt an, was als Mantisse oder Bruchteil bezeichnet wird, der dann vom Exponententeil (implizit 2 ^ exp) MULTIPLIZIERT wird, um das Endergebnis zu erhalten.
Schauen Sie hier für eine visuelle Erklärung der Bits.
Gerade weil dieser Exponent ein tatsächlicher Teil der Bits ist, beginnt die Genauigkeit zu schwinden, sobald die Zahlen größer werden.
Um dies in Aktion zu sehen, lassen Sie uns eine Faux-Floating-Point-Repräsentation durchführen, ohne auf das Wesentliche einzugehen: Nehmen Sie einen kleinen Exponenten wie 2 und führen Sie einige Bruchteile zum Testen durch:
2 * 2 ^ 2 = 8
3 * 2 ^ 2 = 12
4 * 2 ^ 2 = 16
...etc.
Diese Zahlen wachsen nur bei Exponent 2 nicht sehr weit auseinander. Versuchen wir nun Exponent 38:
2 * 2 ^ 38 = 549755813888
3 * 2 ^ 38 = 824633720832
4 * 2 ^ 38 = 1099511627776
Whoa, großer Unterschied jetzt!
Das Beispiel geht zwar nicht speziell auf die SEHR NÄCHSTE ZÄHLBARE (das wäre der nächste Bruchteil, abhängig von der Anzahl der Bits), zeigt aber den Präzisionsverlust, sobald die Zahlen größer werden. Die "next countable" -Einheit in floats ist sehr klein mit kleinen Exponenten und SEHR groß mit größeren Exponenten, während sie in ganzen Zahlen IMMER 1 ist.
Der Grund, warum die Methode float origin funktioniert, ist, dass sie all diese potenziell großexponentigen Gleitkommazahlen auf kleinexponentig skaliert, sodass die "nächsten Zählwerte" (Genauigkeit) sehr klein und glücklich sein können.
quelle
Weil Gleitkommazahlen als Bruch + Exponent + Vorzeichen dargestellt werden und Sie nur eine feste Anzahl von Bits für den Bruchteil haben.
http://en.wikipedia.org/wiki/Single_precision
Wenn Sie immer größere Zahlen erhalten, haben Sie einfach nicht die Bits, um die kleineren Teile darzustellen.
quelle
Der Klassiker auf diesem Gebiet muss angesprochen werden: Was jeder Informatiker über Gleitkommazahlen wissen sollte .
Das Wesentliche dabei ist jedoch, dass Gleitkommazahlen mit einfacher (doppelter) Genauigkeit nur eine 32-Bit-Binärzahl (64-Bit-Binärzahl) sind, wobei 1 Bit das Vorzeichen darstellt, ein 8-Bit-Exponent (11-Bit-Exponent) der Basis 2 und eine 23-Bit-Bedeutung (52-Bit) (die Klammern sind die Werte für Doubles).
Das heißt, die kleinste positive Zahl, die Sie mit einfacher Genauigkeit darstellen können, ist 0,00000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .
Die nächste positive Zahl ist doppelt so groß wie: 0,00000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , und dann ist die nächste Zahl die Summe der beiden vorherigen 0,0000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4,2 - 45 .
Dies nimmt weiterhin um die gleiche konstante Differenz zu, bis: 0,11111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1,17549435 x 10 -38 - 0,00000014 x 10 -38 = 1,17549421 x 10 -38
Jetzt haben Sie die normalen Zahlen erreicht (wobei die erste Ziffer in der Bedeutung 1 ist): 1,0000000000000000000000 x 2 -126 = 2 -126 = 1,17549435 x 10 -38 und die nächste Zahl ist dann 1,000000000000000001 x 2 -126 = 2 -126 (1 + 2 - 22 ) = 1,17549435 x 1,00000023.
quelle
Der Grund, warum Gleitkommazahlen weiter vom Ursprung entfernt ungenauer werden, ist, dass eine Gleitkommazahl große Zahlen darstellen kann. Die Art und Weise, wie dies gemacht wird, verleiht ihm den Begriff "Gleitkomma". Es teilt die möglichen Werte auf, die es annehmen kann (was durch seine Bitlänge bestimmt wird), so dass es für jeden Exponenten ungefähr dieselbe Zahl gibt: Bei einem 32-Bit-Float definieren 23 der Bits die Mantisse oder den Signifikanten. So kann es in jedem Exponentenbereich den Wert von 2 ^ 23 verschiedenen Werten annehmen. Einer dieser Exponentenbereiche ist 1-2 [2 ^ 0 bis 2 ^ 1], so dass die Aufteilung des Bereichs 1 bis 2 in 2 ^ 23 verschiedene Werte viel Präzision ermöglicht.
Wenn Sie jedoch den Bereich [2 ^ 10 bis 2 ^ 11] in 2 ^ 23 verschiedene Werte aufteilen, ist der Abstand zwischen den einzelnen Werten viel größer. Wenn dies nicht der Fall wäre, würden 23 Bits nicht ausreichen. Das Ganze ist ein Kompromiss: Sie brauchen unendlich viele Bits, um eine reelle Zahl darzustellen. Wenn Ihre Anwendung so funktioniert, dass Sie bei größeren Werten mit geringerer Genauigkeit davonkommen und von der Möglichkeit profitieren, große Werte tatsächlich darzustellen , verwenden Sie eine Gleitkommadarstellung.
quelle
Es kann etwas schwierig sein, konkrete Beispiele für die Funktionsweise der Gleitkommapräzision anzubieten. Um die anderen Antworten zu ergänzen, hier eine. Angenommen, wir haben eine dezimale Gleitkommazahl mit drei Stellen der Mantisse und einer Stelle des Exponenten:
Wenn der Exponent 0 ist, kann jede Ganzzahl im Bereich von 0 bis 999 genau dargestellt werden. Wenn es 1 ist, multiplizieren Sie im Wesentlichen jedes Element dieses Bereichs mit 10, sodass Sie den Bereich 0–9990 erhalten. Jetzt können jedoch nur Vielfache von 10 genau dargestellt werden, da Sie immer noch nur drei Stellen Genauigkeit haben. Wenn der Exponent maximal 9 ist, beträgt die Differenz zwischen jedem Paar darstellbarer ganzer Zahlen eine Milliarde . Sie tauschen buchstäblich Präzision gegen Reichweite.
Bei binären Gleitkommazahlen funktioniert dies genauso: Wenn der Exponent um eins erhöht wird , verdoppelt sich der Bereich , aber die Anzahl der darstellbaren Werte innerhalb dieses Bereichs halbiert sich . Dies gilt auch für gebrochene Zahlen, was natürlich die Ursache Ihres Problems ist.
quelle
Im Allgemeinen wird die Auflösung schlechter, da die Auflösung mit dem Exponentenwert (2 ** Exponententeil) multipliziert wird.
zur anerkennung von joshs kommentar: das obige war nur, um die antwort in eine kurze aussage zu fassen. Natürlich, wie ich auf http://floatingorigin.com/ angedeutet habe , ist dies erst der Anfang einer Gesamtlösung, und Ihr Programm könnte an mehreren Stellen zittern: in der Präzisions-Pipeline oder in anderen Teilen des Codes .
quelle
OpenGL-Tiefenpuffer ist nicht linear . Je weiter Sie fortfahren, desto schlechter ist die Auflösung. Ich empfehle lesen diese . Etwas von dort genommen (12.070):
Und noch eine (12.040):
Sie sollten also Ihr nahes Clipping-Flugzeug so weit wie möglich und Ihr fernes Flugzeug so weit wie möglich bewegen.
quelle