Моя сцена OpenGL содержит объекты, которые расположены на смехотворно большом расстоянии от источника. Когда я рассматриваю эти объекты и поворачиваю / поворачиваю / масштабирую камеру вокруг них, они «дрожат». То есть вершины, составляющие объекты, кажутся привязанными к воображаемой трехмерной сетке точек. Я читал, что это распространенная проблема из-за большого количества информации, которая может быть сохранена с использованием точности с плавающей запятой (которую использует OpenGL и почти все остальное). Я не понимаю, почему это происходит, хотя.
При поиске решения я наткнулся на очень простое исправление «плавающее начало», и оно, похоже, работает. Я просто трансформирую все, чтобы мои объекты находились в одинаковых относительных положениях, но то, на что смотрит моя камера, близко к началу координат. Я нашел объяснение здесь: http://floatingorigin.com/ , но я не мог следовать ему.
Итак ... Может ли кто-нибудь объяснить, почему расположение моей сцены очень далеко (скажем, 10 миллионов единиц) от источника приводит к неустойчивому поведению, которое я наблюдал? А также почему перемещение его ближе к источнику решает проблему?
Ответы:
Это ВСЕ из-за того, как плавающие точки представлены в компьютерах.
Целые числа хранятся довольно просто; каждая единица в точности равна «единице» от «предыдущей», как и следовало ожидать с счетными числами.
С числами с плавающей запятой это не совсем так. Вместо этого, несколько битов указывают на экспоненту, а остальные указывают на то, что известно как мантисса , или дробная часть, которая затем умножается на экспонентную часть (неявно 2 ^ exp) для получения окончательного результата.
Посмотрите здесь для визуального объяснения битов.
Именно из-за того, что этот показатель является действительной частью битов, точность начинает уменьшаться, когда числа увеличиваются.
Чтобы увидеть это в действии, давайте сделаем представление с плавающей запятой, не вдаваясь в подробности: возьмем небольшой показатель степени, такой как 2, и проведем несколько дробных частей для проверки:
2 * 2 ^ 2 = 8
3 * 2 ^ 2 = 12
4 * 2 ^ 2 = 16
...и т.д.
Эти числа не очень сильно расходятся только на показателе 2. Но теперь давайте попробуем показатель 38:
2 * 2 ^ 38 = 549755813888
3 * 2 ^ 38 = 824633720832
4 * 2 ^ 38 = 1099511627776
Вау, огромная разница сейчас!
Этот пример, хотя он и не предназначен специально для ОЧЕНЬ СЛЕДУЮЩЕЙ СЧЕТЧИКИ (это будет следующая дробная часть в зависимости от того, сколько битов), демонстрирует потерю точности при увеличении чисел. «Следующая счетная» единица в числах с плавающей точкой очень мала с маленькими показателями и ОЧЕНЬ велика с большими показателями, тогда как в целых числах это ВСЕГДА 1.
Причина того, что метод источника с плавающей запятой работает, заключается в том, что он масштабирует все эти числа с плавающей запятой с потенциально большим показателем ВНИЗ К маленькому показателю, чтобы «следующие счетные числа» (точность) могли быть очень маленькими и счастливыми.
источник
Потому что числа с плавающей запятой представлены как дробь + экспонента + знак, и у вас есть только фиксированное количество бит для дробной части.
http://en.wikipedia.org/wiki/Single_precision
Когда вы получаете все большие и большие числа, у вас просто не хватает битов для представления меньших частей.
источник
Классика в этой области должна быть воспитана: что должен знать каждый компьютерщик о числах с плавающей запятой .
Но суть этого в том, что числа с плавающей запятой одинарной (двойной) точности представляют собой просто 32-битное (64-битное) двоичное число с 1-битным знаком, представляющим знак, 8-битный (11-битный) показатель степени 2 и 23-битное (52-битное) значениеand (в скобках указаны значения для двойных чисел).
Это означает, что наименьшее положительное число, которое вы можете представить с одинарной точностью, составляет 0,0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .
Следующее положительное число в два раза больше: 0,0000000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , а затем следующее число является суммой двух предыдущих 0,0000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4.2 - 45 .
Это продолжает увеличиваться на ту же постоянную разницу до тех пор, пока: 0,1111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1,17549435 x 10 -38 - 0,00000014 x 10 -38 = 1,17549421 x 10 -38
Теперь вы достигли нормальных чисел (где первая цифра мантиссы 1) а именно: +1,0000000000000000000000 х 2 -126 = 2 -126 = 1,17549435 х 10 -38 и следующее число , то +1,0000000000000000000001 х 2 -126 = 2 -126 (1 + 2 -22 ) = 1,17549435 х 1,00000023.
источник
Причина, по которой числа с плавающей запятой становятся менее точными вдали от источника, заключается в том, что число с плавающей запятой должно представлять большие числа. То, как это делается, дает ему термин «с плавающей точкой». Он разделяет возможные значения, которые он может принимать (который определяется его длиной в битах), так что для каждого показателя степени примерно одинаковое число: для 32-разрядного числа с плавающей запятой 23 из битов определяют мантиссу или значение. Таким образом, он сможет принимать значение 2 ^ 23 различных значений в каждом диапазоне показателей. Один из этих диапазонов экспонент равен 1-2 [2 ^ 0 до 2 ^ 1], поэтому разбиение диапазона от 1 до 2 на 2 ^ 23 различных значений обеспечивает большую точность.
Но разделение диапазона [2 ^ 10 до 2 ^ 11] на 2 ^ 23 различных значений означает, что расстояние между каждым значением намного больше. Если бы этого не было, то 23 бита было бы недостаточно. Все дело в компромиссе: вам нужно бесконечное количество бит для представления любого действительного числа. Если ваше приложение работает таким образом, что позволяет вам обходиться с более низкой точностью при больших значениях, и вы извлекаете выгоду из возможности фактически представлять большие значения , вы используете представление с плавающей запятой.
источник
Может быть немного сложно предложить конкретные примеры того, как работает точность с плавающей точкой. Чтобы дополнить другие ответы, вот один. Допустим, у нас есть десятичное число с плавающей точкой, с тремя цифрами мантиссы и одной цифрой экспоненты:
Когда показатель степени равен 0, каждое целое число в диапазоне 0–999 может быть представлено точно. Когда это 1, вы, по сути, умножаете каждый элемент этого диапазона на 10, так что вы получаете диапазон 0–9990; но теперь, только кратные 10 могут быть представлены точно, потому что у вас все еще есть только три цифры точности. Когда показатель степени равен максимуму 9, разница между каждой парой представимых целых чисел составляет один миллиард . Вы буквально торгуете точностью ради дальности.
Он работает так же с двоичными числами с плавающей запятой: всякий раз, когда показатель степени увеличивается на единицу, диапазон удваивается , но число представляемых значений в этом диапазоне уменьшается вдвое . Это относится и к дробным числам, что, конечно, является источником вашей проблемы.
источник
В общем, разрешение ухудшается, потому что разрешение умножается на значение показателя степени (2 ** часть показателя).
в подтверждение комментария Джоша: вышеупомянутое было просто, чтобы поместить ответ в краткое заявление. Конечно, как я пытался указать на http://floatingorigin.com/ , это только начало общего решения, и ваша программа может иметь дрожание из ряда мест: в конвейере точности или других частях кода. ,
источник
Буфер глубины OpenGL не является линейным . Чем дальше, тем хуже разрешение. Я рекомендую прочитать это . Что-то взято оттуда (12.070):
И еще один (12.040):
Таким образом, вы должны переместить свой ближний отсеченный самолет как можно дальше, а дальний - как можно ближе.
источник