Как рассчитать радиус конуса обзора (т.е. размер пикселя) на расстоянии в raymarching?

Я «просматриваю поля расстояний» (правильный жаргон: трассировка сфер) в GLSL. Чтобы реализовать конусное марширование поверх него (а также свести к минимуму количество шагов raymarching независимо от того, добавлено ли конусное марширование или нет), мне нужно оценить радиус конусного луча на любом заданном расстоянии.

Вспомним, что с полями расстояний raymarching «попадание» записывается, когда расстояние до объекта меньше порогового значения, часто в коде с именем nearLimit или epsilon. Этот порог можно рассматривать как эквивалент радиуса конуса луча, если мы увеличиваем его экспоненциально с пройденным расстоянием — таким образом, мы выпускаем в пространство не прямые тонкие лучевые линии, а конусы, расширяющиеся в соответствии с перспективной проекцией. Это более точно охватывает захват «правильных» удаленных объектов (на данный момент давайте пока проигнорируем проблему смешивания материалов и фильтрации нормалей всех пересекающихся объектов в конусе обзора на расстоянии t...).

На шаге 0 этот радиус может быть аппроксимирован чем-то вроде

float fInitialRadius = 1 / min(screenwidth, screenheight);

Затем его можно экспоненциально увеличивать на каждом шаге, применяя начальный радиус к расстоянию:

fNearLimit = fTotalDist * fInitialRadius;  // after each raymarching step

Это работает нормально, но все еще имеет артефакты. Если я использую fInitialRadius*fInitialRadius (в результате получается меньшее число, поскольку начальный радиус для фреймбуфера 640 пикселей и плоскости просмотра единичной ширины составляет 1/640), я получаю меньше артефактов и более точный результат. Но оба подхода неточны, первый слишком рьяный (увеличивает радиус слишком сильно и слишком рано), второй слишком ленив (увеличивает радиус слишком мало и слишком поздно).

Наиболее точный фактор для увеличения fNearLimit/радиуса конуса на заданном расстоянии, скорее всего, должен учитывать мое текущее поле зрения и будет варьироваться в зависимости от того, составляет ли поле зрения 45°, 60°, 90° или...

TL;DR: я хочу знать, каков правильный расчет или наиболее приемлемая аппроксимация радиуса конуса на заданном расстоянии с учетом начального радиуса пикселя на шаге 0 и поля угол обзора?


person metaleap    schedule 08.05.2012    source источник
comment
Я хотел бы отметить, что пиксель обычно представляет собой квадрат, на самом деле конус должен быть усеченным. Тогда размер будет линейным по отношению к расстоянию, а не экспоненциальным.   -  person Luca    schedule 08.05.2012
comment
Спасибо, Лука, хорошая мысль! Давайте немного упростим ситуацию. Чтобы реализовать марш-бросок конуса поверх трассировки лучей/трассировки сфер, мне нужно было бы хотя бы приблизиться к подходящему радиусу, который ошибается на безопасной стороне — то, что происходит здесь при марш-броске конуса, — это сравнение размера дистанционной сферы на текущем шаге. position к текущей ширине конуса. Но...   -  person metaleap    schedule 08.05.2012
comment
... на мгновение игнорируя приближение радиального конуса / пиксельного сферического диска и рассматривая усеченную прямоугольную проекцию. Для плоскости просмотра w мировых единиц ширины и проекции на экранные единицы 320px * 320px общая ширина усеченного конуса на расстоянии t должна быть t * w * tan(fovRadians * 0,5), правильно? (... или умножить на 1/tan... или деление на тангенс... или деление на 1/тангенс? :)) Для fov=90° весь тангенс (или 1/тангенс и т. д.) равен 1, поэтому т * ш. (ширина = высота на данный момент. Позже возьмите минимум обоих.)   -  person metaleap    schedule 08.05.2012
comment
Таким образом, для заданного пикселя p прямоугольная часть всей усеченной пирамиды, увеличенной как показано выше, на этом расстоянии t равна t * w / 320. Чтобы ошибиться на всякий случай, мой конусный круг должен поместиться внутри этот прямоугольник, но не перекрывает его, поэтому радиус = min(pixelRectInFrustumWidth, pixelRectInFrustumHeight)/2.   -  person metaleap    schedule 08.05.2012
comment
Видите какие-либо проблемы с этим рассуждением? Это сложно проверить визуально, так как я так или иначе получаю артефакты сглаживания, в основном по краям, что заставляет меня думать, что я не должен использовать последний радиус конуса, записанный как нормальный эпсилон... но это другая тема для другой день. Прямо сейчас я не совсем уверен, следует ли умножать или делить либо на тангенс fov/2, либо на 1/тангенс этого.   -  person metaleap    schedule 08.05.2012


Ответы (1)


Радиус конуса линейно соответствует расстоянию от его вершины. (Иначе это не конус!)

Итак, если ваш конус имеет initialRadius, когда он пересекает плоскость экрана, то позже:

radius(distance) = distance * initialRadius / focalDistance

Вам придется пересчитывать это значение на каждом шаге, потому что каждый шаг уносит вас на разное расстояние.

Здесь distance — расстояние луча от камеры, а focalDistance — расстояние плоскости экрана от камеры.

(Для пикселей не в центре экрана вместо focalDistance может быть точнее использовать расстояние от пикселя на плоскости экрана до камеры.)

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

person joeytwiddle    schedule 05.07.2012