Mi escena OpenGL tiene objetos que se colocan a distancias ridículamente lejanas del origen. Cuando veo estos objetos, y desplazo / giro / acerco una cámara a su alrededor, 'vibran'. Es decir, los vértices que comprenden los objetos parecen romperse alrededor de una grilla imaginaria de puntos en 3D. He leído que este es un problema común debido a la cantidad de información que se puede almacenar con precisión de punto flotante (que OpenGL, y casi todo lo demás usa). Sin embargo, no entiendo por qué sucede esto.
Al buscar una solución, me encontré con la solución muy simple de 'origen flotante', y parece funcionar. Simplemente transformo todo para que mis objetos estén en las mismas posiciones relativas, pero cualquier cosa que mi cámara esté mirando está cerca del origen. Encontré una explicación aquí: http://floatingorigin.com/ , pero no pude seguirla.
Entonces ... ¿Podría alguien explicar por qué posicionar mi escena muy lejos (digamos 10 millones de unidades) del origen da como resultado el comportamiento errático que observé? ¿Y también por qué acercarlo al origen soluciona el problema?
Respuestas:
Todo esto se debe a la forma en que se representan los puntos flotantes en las computadoras.
Los enteros se almacenan de forma bastante sencilla; cada unidad es exactamente "una" aparte de la "anterior" tal como cabría esperar con números contables.
Con números de coma flotante, este no es exactamente el caso. En cambio, varios bits indican el EXPONENTE, y el resto indica lo que se conoce como mantisa , o parte fraccionaria que luego es MULTIPLICADA por la parte exponente (implícitamente 2 ^ exp) para dar el resultado final.
Busque aquí una explicación visual de los bits.
Precisamente debido a que este exponente es una parte real de los bits, la precisión comienza a GANAR una vez que los números crecen.
Para ver esto en acción, hagamos una representación de punto flotante falso sin entrar en el meollo del asunto: tome un pequeño exponente como 2 y haga algunas partes fraccionarias para probar:
2 * 2 ^ 2 = 8
3 * 2 ^ 2 = 12
4 * 2 ^ 2 = 16
... etc.
Estos números no crecen muy separados en el único exponente 2. Pero ahora intentemos con el exponente 38:
2 * 2 ^ 38 = 549755813888
3 * 2 ^ 38 = 824633720832
4 * 2 ^ 38 = 1099511627776
Whoa, gran diferencia ahora!
El ejemplo, aunque no va específicamente al MUY PRÓXIMO CONTABLE (que sería la siguiente parte fraccionaria dependiendo de cuántos bits sean), está ahí para demostrar la pérdida de precisión una vez que los números crecen. La unidad "siguiente contable" en flotadores es muy pequeña con exponentes pequeños y MUY grande con exponentes más grandes, mientras que en enteros SIEMPRE es 1.
La razón por la que funciona el método de origen flotante es porque está escalando todos estos números de punto flotante de exponente potencialmente grande DOWN TO small-exponent para que los "próximos contables" (precisión) puedan ser muy pequeños y felices.
fuente
Debido a que los números de coma flotante se representan como fracción + exponente + signo, y solo tiene una cantidad fija de bits para la parte de fracción.
http://en.wikipedia.org/wiki/Single_precision
A medida que obtiene números cada vez más grandes, simplemente no tiene los bits para representar las porciones más pequeñas.
fuente
Debe mencionarse el clásico en el campo: lo que todo informático debe saber sobre los números de coma flotante .
Pero lo esencial tiene que ver con cómo los números de coma flotante de precisión simple (doble) son solo un número binario de 32 bits (64 bits) con 1 bit que representa el signo, un exponente de 8 bits (11 bits) de la base 2 , y un significado de 23 bits (52 bits) (los paréntesis son los valores para dobles).
Eso significa que el número positivo más pequeño que puede representar con precisión simple es 0.0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1.40 x 10 -45 .
El siguiente número positivo es el doble que: 0.0000000000000000000010 x 2 -127 = 2 -148 ~ 2.80 x 10 -45 , y luego el siguiente número es la suma de los dos anteriores 0.0000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4.2 - 45 .
Esto continúa aumentando por la misma diferencia constante hasta: 0.1111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1.17549435 x 10 -38 - 0.00000014 x 10 -38 = 1.17549421 x 10 -38
Ahora que ha llegado a los números normales (donde el primer dígito en la mantisa es 1), específicamente: 1.0000000000000000000000 x 2 -126 = 2 -126 = 1,17549435 x 10 -38 y el siguiente número es entonces 1.0000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 x 1.00000023.
fuente
La razón por la cual los números de punto flotante se vuelven menos precisos más lejos del origen es porque se supone que un número de punto flotante puede representar números grandes. La forma en que se hace esto le da el término "punto flotante". Divide los posibles valores que puede tomar (que está determinado por su longitud de bits) para que haya aproximadamente el mismo número para cada exponente: para un flotante de 32 bits, 23 de los bits definen la mantisa o significado. Por lo tanto, podrá tomar el valor de 2 ^ 23 valores diferentes en cada rango de exponente. Uno de estos rangos de exponente es 1-2 [2 ^ 0 a 2 ^ 1], por lo que dividir el rango 1 a 2 en 2 ^ 23 valores diferentes permite mucha precisión.
Pero dividir el rango [2 ^ 10 a 2 ^ 11] en 2 ^ 23 valores diferentes significa que el espacio entre cada valor es mucho mayor. Si no fuera así, entonces 23 bits no serían suficientes. Todo es un compromiso: necesita un número infinito de bits para representar cualquier número real. Si su aplicación funciona de una manera que le permite escapar con menor precisión para valores más grandes, y se beneficia de poder representar valores grandes , entonces utiliza una representación de punto flotante.
fuente
Puede ser un poco difícil ofrecer ejemplos específicos de cómo funciona la precisión de punto flotante. Para complementar las otras respuestas, aquí hay una. Digamos que tenemos un número decimal de coma flotante, con tres dígitos de mantisa y un dígito de exponente:
Cuando el exponente es 0, cada número entero en el rango 0–999 se puede representar con precisión. Cuando es 1, esencialmente estás multiplicando cada elemento de ese rango por 10, por lo que obtienes el rango 0–9990; pero ahora, solo se pueden representar con precisión múltiplos de 10, ya que solo tiene tres dígitos de precisión. Cuando el exponente está en su máximo de 9, la diferencia entre cada par de enteros representables es mil millones . Literalmente, está cambiando la precisión por el rango.
Funciona de la misma manera con números binarios de punto flotante: cada vez que el exponente aumenta en uno, el rango se duplica , pero el número de valores representables dentro de ese rango se reduce a la mitad . Esto también se aplica a los números fraccionarios, que por supuesto es la fuente de su problema.
fuente
En general, la resolución empeora porque la resolución se multiplica por el valor del exponente (2 ** parte del exponente).
en reconocimiento del comentario de josh: lo anterior fue solo para poner la respuesta en una declaración sucinta. Por supuesto, como he tratado de indicar en http://floatingorigin.com/ , esto solo está comenzando hacia una solución general y su programa podría tener fluctuaciones de varios lugares: en la tubería de precisión u otras partes del código .
fuente
El tampón de profundidad OpenGL no es lineal . Cuanto más lejos vayas, peor resolución tiene. Recomiendo leer esto . Algo tomado de allí (12.070):
Y otro (12.040):
Por lo tanto, debe mover su plano de recorte cercano lo más lejos que pueda y su plano lejano lo más cerca que pueda.
fuente