Minha cena do OpenGL possui objetos que são posicionados a distâncias ridiculamente distantes da origem. Quando visualizo esses objetos e giro / giro / zoom uma câmera ao redor deles, eles 'tremem'. Ou seja, os vértices que compõem os objetos parecem se encaixar em uma grade 3d imaginária de pontos. Eu li que esse é um problema comum devido à quantidade de informações que podem ser armazenadas usando a precisão do ponto flutuante (que o OpenGL e praticamente todo o resto usa). Eu não entendo por que isso acontece embora.
Ao procurar uma solução, deparei-me com a correção muito simples de 'origem flutuante', e ela parece funcionar. Eu apenas transformo tudo para que meus objetos estejam nas mesmas posições relativas, mas o que quer que minha câmera esteja perto da origem. Encontrei uma explicação aqui: http://floatingorigin.com/ , mas não consegui segui-la.
Então ... Alguém poderia explicar por que posicionar minha cena muito longe (digamos, 10 milhões de unidades) da origem resulta no comportamento errático que observei? E também por que movê-lo para perto da origem resolve o problema?
Respostas:
Isso é tudo devido à maneira como os pontos flutuantes são representados nos computadores.
Os números inteiros são armazenados de maneira bastante direta; cada unidade é exatamente "uma", além da "anterior", como seria de esperar com números contáveis.
Com números de ponto flutuante, esse não é exatamente o caso. Em vez disso, vários bits indicam o EXPONENTE, e o restante indica o que é conhecido como mantissa , ou parte fracionária que é então MULTIPLICADA pela parte do expoente (implicitamente 2 ^ exp) para fornecer o resultado final.
Procure aqui uma explicação visual dos bits.
É precisamente por esse expoente ser uma parte real dos bits que a precisão começa a diminuir quando os números aumentam.
Para ver isso em ação, vamos fazer uma representação de ponto flutuante falso sem entrar no âmago da questão: pegue um pequeno expoente como 2 e faça algumas partes fracionárias para testar:
2 * 2 ^ 2 = 8
3 * 2 ^ 2 = 12
4 * 2 ^ 2 = 16
... etc
Esses números não crescem muito separados apenas no expoente 2. Mas agora vamos tentar o expoente 38:
2 * 2 ^ 38 = 549755813888
3 * 2 ^ 38 = 824633720832
4 * 2 ^ 38 = 1099511627776
Whoa, enorme diferença agora!
O exemplo, embora não vá especificamente para MUITO PRÓXIMO CONTÁBIL (que seria a próxima parte fracionária dependendo de quantos bits são), existe para demonstrar a perda de precisão quando os números aumentam. A unidade "próximo contável" em carros alegóricos é muito pequena com expoentes pequenos e MUITO grande com expoentes maiores, enquanto em números inteiros é SEMPRE 1.
A razão pela qual o método de origem de flutuação funciona é porque ele está dimensionando todos esses números de ponto flutuante de expoente potencialmente grande PARA BAIXO PARA expoente pequeno para que os "próximos contáveis" (precisão) possam ser muito pequenos e felizes.
fonte
Como os números de ponto flutuante são representados como fração + expoente + sinal, e você só tem uma quantidade fixa de bits para a parte da fração.
http://en.wikipedia.org/wiki/Single_precision
À medida que você obtém números cada vez maiores, você simplesmente não possui os bits para representar as partes menores.
fonte
O clássico no campo deve ser mencionado: o que todo cientista da computação deve saber sobre números de ponto flutuante .
Mas a essência disso tem a ver com como os números de ponto flutuante de precisão simples (dupla) são apenas um número binário de 32 bits (64 bits), com 1 bit representando o sinal, um expoente de 8 bits (11 bits) da base 2 e um significando de 23 bits (52 bits) (os parênteses são os valores para o dobro).
Isso significa que o menor número positivo que você pode representar com precisão única é 0,0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .
O próximo número positivo é o dobro que: 0,0000000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 e, em seguida, o próximo número é a soma dos dois anteriores 0,00000000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4.2 - 45 .
Isso continua aumentando pela mesma diferença constante até: 0,11111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1,117549435 x 10 -38 - 0,00000014 x 10 -38 = 1,117549421 x 10 -38
Agora você alcançou os números normais (onde o primeiro dígito no significando é 1) especificamente: 1.00000000000000000000000000 x 2 -126 = 2 -126 = 1.17549435 x 10 -38 e o próximo número é então 1.000000000000000000000000 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1,17549435 x 1,00000023.
fonte
A razão pela qual os números de ponto flutuante se tornam menos precisos ainda mais a partir da origem é porque um número de ponto flutuante deve ser capaz de representar números grandes. A maneira como isso é feito empresta o termo "ponto flutuante". Ele divide os possíveis valores que pode ser obtido (que é determinado pelo comprimento de bits) para que exista aproximadamente o mesmo número para cada expoente: para um flutuador de 32 bits, 23 dos bits definem a mantissa ou significando. Portanto, ele poderá assumir o valor de 2 ^ 23 valores diferentes em cada intervalo de expoente. Um desses intervalos de expoente é 1-2 [2 ^ 0 a 2 ^ 1], portanto, dividir o intervalo de 1 a 2 em 2 ^ 23 valores diferentes permite muita precisão.
Mas dividir o intervalo [2 ^ 10 a 2 ^ 11] em 2 ^ 23 valores diferentes significa que o espaço entre cada valor é muito maior. Se não fosse, então 23 bits não seriam suficientes. A coisa toda é um compromisso: você precisa de um número infinito de bits para representar qualquer número real. Se seu aplicativo funcionar de uma maneira que permita uma precisão menor para valores maiores, e você se beneficiar de poder realmente representar valores grandes , use uma representação de ponto flutuante.
fonte
Pode ser um pouco difícil oferecer exemplos específicos de como a precisão de ponto flutuante funciona. Para complementar as outras respostas, aqui está uma. Digamos que tenhamos um número decimal de ponto flutuante, com três dígitos de mantissa e um dígito de expoente:
Quando o expoente é 0, todos os números inteiros no intervalo de 0 a 999 podem ser representados com precisão. Quando é 1, você está basicamente multiplicando cada elemento desse intervalo por 10, para obter o intervalo de 0 a 9990; mas agora, apenas múltiplos de 10 podem ser representados com precisão, porque você ainda tem apenas três dígitos de precisão. Quando o expoente atinge o máximo de 9, a diferença entre cada par de números inteiros representáveis é de um bilhão . Você está literalmente trocando precisão por alcance.
Funciona da mesma maneira com números binários de ponto flutuante: sempre que o expoente aumenta um, o intervalo dobra , mas o número de valores representáveis nesse intervalo é reduzido pela metade . Isso também se aplica a números fracionários, que é obviamente a fonte do seu problema.
fonte
Em geral, a resolução piora porque é multiplicada pelo valor do expoente (2 ** parte do expoente).
em reconhecimento ao comentário de Josué: o acima foi apenas para colocar a resposta em uma declaração sucinta. Obviamente, como tentei indicar em http://floatingorigin.com/ , isso está apenas começando com uma solução geral e seu programa pode ter tremores de vários lugares: no pipeline de precisão ou em outras partes do código .
fonte
O buffer de profundidade do OpenGL não é linear . Quanto mais longe você for, pior será a resolução. Eu recomendo ler isso . Algo retirado de lá (12.070):
E outro (12.040):
Portanto, mova o plano de recorte próximo o mais longe possível e o plano mais distante o mais próximo possível.
fonte