OpenGL — gluPerspective / glFrustum — проблемы zNear и zFar

Я пишу приложение для исследования космоса. Я решил, что световые годы являются единицами измерения, и точно смоделировал расстояния между звездами. После возни и много тяжелой работы (в основном, изучения веревок) я заставил камеру работать правильно с точки зрения космического корабля, пересекающего космос.

Изначально я не обращал внимания на параметр zNear функции gluPerspective(), пока не начал работать с планетарными объектами. Поскольку моя шкала измеряется в световых годах, я вскоре понял, что из-за того, что zNear равен 1,0f, я не смогу видеть такие объекты. После экспериментов я пришел к таким цифрам:

#define POV 45
#define zNear 0.0000001f
#define zFar 100000000.0f
gluPerspective (POV, WinWidth/WinHeight, zNear ,zFar);

Это работает исключительно хорошо, поскольку я смог совершить круиз по своей солнечной системе (позиция 0,0,0) и приблизиться к планетам, которые выглядят великолепно освещенными и текстурированными. Однако через другие системы (не в позиции 0,0,0) пройти было намного сложнее, потому что объекты удалялись от камеры необычным образом.

Однако я заметил, что во время путешествия по вселенной начали происходить странные визуальные сбои. Объекты позади меня будут «заворачиваться» и показываться впереди, если я повернусь на 180 градусов в направлении Y, они также появятся на своем первоначальном месте. Таким образом, при искривлении пространства большинство звезд правильно параллаксируют, но некоторые появляются и движутся в противоположном направлении (что, мягко говоря, настораживает).

Изменение zNear на 0.1f немедленно исправляет ВСЕ эти глюки (но также не разрешает объекты Солнечной системы). Так что я застрял. Я также пробовал работать с glFrustum, и он дает точно такие же результаты.

Я использую следующее для просмотра мира:

glTranslatef(pos_x, pos_y, pos_z);

С соответствующим кодом камеры для ориентации по мере необходимости. Даже отключение функционала камеры ничего не меняет. Я даже пробовал gluLookAt(), и снова он дает те же результаты.

Есть ли у gluPerspective() ограничения при использовании экстремальных значений zNear/zFar? Я пытался уменьшить диапазон, но безрезультатно. Я даже изменил свои мировые единицы со световых лет на километры, увеличив масштаб и используя большее значение zNear — ничего. ПОМОЩЬ!


person Curious Coder    schedule 07.09.2011    source источник
comment
заметил, однако, что странные визуальные сбои, каковы были координаты вашей камеры в мире, когда это произошло? я подозреваю, что эти проблемы вызваны точностью поплавка. вы могли бы исправить это (слегка), уменьшив свой мир, но это не исправит его полностью.   -  person Rookie    schedule 07.09.2011
comment
Ну, один очевидный сбой произошел при пересечении оси Z с плюса на минус. Я не мог понять, почему это могло произойти, если код работал раньше, пока zNear не упал. Поэтому я подумал, что быстрое исправление будет смещать все координаты, чтобы они оставались положительными. Это не помогло, поэтому я предполагаю, что упомянутый переход был просто совпадением. Бывает и в других местах. Я узнаю подробности, если потребуется.   -  person Curious Coder    schedule 07.09.2011
comment
Ваша следующая проблема будет заключаться в том, что, хотя float может отображать огромные расстояния, у него очень мало разрешения. Это двоичная версия экспоненциальной записи с фиксированным количеством значащих цифр. Если у вас есть звезда на расстоянии 4,0 LY и планета на расстоянии 1,5e-5 LY, местоположение планеты будет на расстоянии 4,0-1,5e-5 = 4,0 LY, упс.   -  person Ben Jackson    schedule 11.09.2011
comment
Да, другие ответили на все это, но я хотел бы добавить, что то, что вы пишете, кажется действительно крутым, и я хотел бы увидеть готовый продукт. Так держать!   -  person Cosine    schedule 29.08.2013


Ответы (3)


Проблема в том, что вы хотите решить слишком многое одновременно. Вы хотите рассматривать вещи в масштабе Солнечной системы, а также в полугалактическом масштабе. Это просто невозможно. Не с рендерером в реальном времени.

Существует только так много точности с плавающей запятой. А так как ваш zNear находится невероятно близко, вы фактически уничтожили свой буфер глубины для всего, что находится на расстоянии более 0,0001 от вашей камеры.

Что вам нужно сделать, так это нарисовать вещи в зависимости от расстояния. Близкие объекты (в масштабе Солнечной системы) рисуются с одной матрицей перспективы, с использованием одного диапазона глубины (скажем, от 0 до 0,8). Затем более удаленные объекты рисуются с другой матрицей перспективы и другим диапазоном глубины (от 0,8 до 1). Это действительно единственные способы заставить эту работу работать.

Кроме того, вам может потребоваться вычислить матрицы для объектов на ЦП в математике с двойной точностью, а затем преобразовать их обратно в одинарную точность для использования OpenGL.

person Nicol Bolas    schedule 07.09.2011
comment
Я предполагал, что это может иметь место, и пытался иметь две точки зрения, как вы предложили. Поэтому, находясь рядом со звездной системой, просто переключитесь на новые значения, которые заставят все звезды исчезнуть (не идеально, но это поведение, которое я бы исключил, так что все в порядке). Я попробовал другой подход к параметрам, получил странные результаты. На макушке я пробовал: от 0,0000001f до 1,0f для близкого 1,0f до 100000000,0f для далекого Но это не решило запутанную ориентацию; при приближении к объекту расстояние до него уменьшается, а затем увеличивается, в то время как объект отклоняется от центра! Я попробую предложенные вами значения. - person Curious Coder; 07.09.2011
comment
Ваши значения сработали лучше, и, к моему удивлению, звезды не исчезают при переходе в режим звездной системы. Я обнаружил, что странным поведением было неправильное отображение НЕ пикселей, а соответствующей текстовой метки с использованием glRasterPos3f() и glutBitmapCharacter(). Я в недоумении, почему это так себя ведет, когда соотношение zNear/zFar зашкаливает! Любые указатели будут оценены. - person Curious Coder; 08.09.2011
comment
@CuriousCoder проверьте OpenGL FAQ для Z-буфера и Google znear zfar, все должно быть ясно. - person kaoD; 05.12.2011

OpenGL не должен отрисовывать ничего дальше от камеры, чем zFar, или ближе к камере, чем zNear.

Но для промежуточных целей OpenGL вычисляет значение глубины, которое сохраняется в буфере глубины и используется для определения того, блокирует ли один объект другой. К сожалению, буфер глубины имеет ограниченную точность (обычно 16 или 24 бита) и согласно этому теряется примерно log2(zFar/zNear) битов точности. Таким образом, соотношение zFar/zNear, равное 10^15 (потеря ~50 бит), неизбежно вызовет проблемы. Одним из вариантов было бы немного увеличить zNear (если вы можете). В противном случае вам нужно будет изучить раздельные буферы глубины или логарифмические буферы глубины.

person fintelia    schedule 11.09.2011
comment
Это ответ, который наиболее близок к сути дела: zNear и zFar — это не просто параметры отсечения: они используются для масштабирования z-буфера. - person Ben Jackson; 11.09.2011

Никол Болас уже рассказал вам одну часть истории. Во-вторых, вы должны начать думать о структурированном способе хранения координат: сохранить положение каждого объекта по отношению к объекту, который доминирует над ним гравитационно, и использовать для них соответствующие единицы измерения.

Итак, у вас есть звезды. Расстояния между звездами измеряются в световых годах. Звезды вращаются вокруг планет. Расстояния внутри звездной системы измеряются от световых минут до световых часов. Планеты вращаются вокруг лун. Расстояния в планетной системе измеряются в световых секундах.

Для отображения таких масштабов нужно рендерить в несколько проходов. Объекты своими масштабами образуют дерево. Сначала вы сортируете ветки от дальних к близким, затем сначала проходите глубину дерева. Для каждого уровня ветвления вы используете соответствующие параметры проекции, чтобы ближняя → дальняя плоскости отсечения точно соответствовали визуализируемым объектам. После рендеринга каждого уровня очищайте буфер глубины.

person datenwolf    schedule 07.09.2011
comment
Спасибо за ваши предложения. К счастью, я понял, что объекты меньшего масштаба должны быть относительно больших, это облегчает работу с их собственными единицами, и если больший движется, меньший следует за ним бесплатно, идеальное поведение для вселенной! Ваше предложение о нескольких проходах и буфере глубины будет полезно, когда я туда доберусь. У меня вроде как все работает в больших масштабах, но есть проблемы в меньших. Я обнаружил, что навигация работает лучше всего относительно 0,0,0 — в любом другом месте она непредсказуема! Удалось ли мне как-то случайно «повернуться» вокруг этих координат? - person Curious Coder; 08.09.2011
comment
@Curious Coder: трудно сказать, не видя реального кода. Порядок операций имеет значение и с каких матриц вы начинаете. - person datenwolf; 08.09.2011