Как я могу быстро создать подписанные поля расстояния (2D) в режиме реального времени?

21

В предыдущем вопросе было предложено предварительно рассчитать поля расстояния со знаком, загрузить их во время выполнения и затем использовать оттуда.

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

Существуют некоторые статьи для различных методов, которые должны быть жизнеспособными в средах реального времени, таких как методы для преобразований расстояния Чамфера и преобразования на основе диаграмм-приближений Вороного (как это предлагается в этой презентации разработчиком Pixeljunk Shooter ), но Мне (и, таким образом, можно предположить, что многие другие люди) действительно очень трудно использовать их, так как они обычно длинные, в основном раздутые с математикой и не очень алгоритмичные в своих объяснениях.

Какой алгоритм вы бы предложили для создания полей расстояний в реальном времени (выгодно для графического процессора), особенно с учетом качества получаемых полей расстояний?

Так как я ищу фактическое объяснение / учебное пособие, а не ссылку на просто еще одну статью или слайд, этот вопрос получит вознаграждение, как только он будет иметь право на один :-).

Вот почему мне нужно сделать это в режиме реального времени:

Если вам необходимо предварительно вычислить эти SDF для больших 2D-сред (например, для большой карты типа Terraria), это будет означать, что вы принимаете довольно большие накладные расходы на пространство хранения (и время генерации карты) в пользу реализации более сложный алгоритм, который достаточно быстр для генерации SDF в реальном времени.

Например, сравнительно небольшая карта с 1000 * 256 (ширина * высота) с размером плитки 10 * 10 пикселей и, следовательно, с общими размерами 10000 * 2560 пикселей, уже обойдется вам примерно в 2 мегабайта, если вы выберете относительно маленький Разрешение SDF 128x128, при условии, что вы храните только значения расстояния от 0 до 255.

Очевидно, что это может быстро стать слишком много, и это накладные расходы, которые я не хочу иметь.

Есть что-то еще:

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

TravisG
источник
Я погуглил «что такое поле расстояния со знаком » и первым нажал на версию для GPU: http.developer.nvidia.com/GPUGems3/gpugems3_ch34.html Это немного устарело , но может помочь в дальнейшем поиске.
Патрик Хьюз
1
Может быть, я что-то упускаю, но меня несколько смущает утверждение, почему вам нужно делать это в режиме реального времени (не в последнюю очередь, почему оно помечено спойлером); во-первых, где взять 2MB для SDF 128x128? Во-вторых, почему вы считаете 2 МБ особенно интенсивным использованием памяти? Я согласен, что это несущественно, но кажется, что это небольшая часть общего использования памяти карты. И в-третьих, как генерация поля в реальном времени сохранит эту память? Вам все еще нужно хранить точно такие же данные, независимо от того, сгенерированы они на лету или предварительно вычислены, нет?
Стивен Стадницки,
В более широком смысле, легко может быть то, что SDF - это не та техника, которая вам нужна. Дополнительная информация о вашей конкретной ситуации - статическом числе препятствий, динамическом числе препятствий и т. Д. - и о том, какого именно эффекта вы надеетесь достичь, будет полезна при попытке определить, что наиболее вероятно будет для вас полезным.
Стивен Стадницки
1
Если бы я генерировал поле расстояния в реальном времени, я бы создавал эти 2 МБ только один раз за кадр (общий объем памяти всегда был бы объемом памяти, необходимым для одного поля расстояния, поскольку мне нужен только один для экрана). Если у меня карта большего размера, чем в моем примере 1000x128 (я думаю, что большие карты Terraria выходят далеко за рамки 10000), мне нужна одна из этих 2 МБ для каждой субкарты 1000x128 этой карты. Почему в первую очередь мне нужны SDF, описано в первом вопросе, который я связал в начале этого вопроса (он предназначен для отбрасывания теней в GPU 2D).
TravisG
1
@heishe вы пытаетесь сгенерировать 2Mb данных один раз за кадр? Шутки в сторону?
kaoD

Ответы:

9

Каталин Зима объясняет, как добиться динамических 2D теней в своей статье - и он использует поле расстояния со знаком (из того, что я могу сказать, это просто причудливое имя для буфера теней в этом контексте). Его метод действительно нуждается в графическом процессоре, и его реализация как есть не самая лучшая (его частота ниже 60 Гц при 20 лампах на моей машине, у моей - около 500 ламп); что и следовало ожидать, поскольку он предпочитал ясность кода по скорости.

Реализация

Именно так, как он реализовал:

  1. Визуализируйте все заклинатели теней в текстуру.
  2. Рассчитайте расстояние до центра источника света для каждого пикселя и присвойте это значение RGB непрозрачных пикселей.
  3. Искажите изображение так, чтобы оно представляло, как трехмерная камера видела эти пиксели.
  4. Раздавите изображение до размера 2xN, используя необычный размер, описанный в его статье (обычный размер не сработает).
  5. Изображение 2xN теперь является вашим полем расстояния со знаком для всех четырех квадрантов света (помните, что один квадрант - это, в основном, единая камера с углом обзора 90 градусов).
  6. Визуализируйте карту освещения.
  7. Размыть карту освещения (в зависимости от расстояния от источника света), чтобы получить мягкие тени.

Моя последняя реализация была (каждый шаг был одним шейдером):

  1. Do (1).
  2. Do (2) и (3) .
  3. Do (4). Его реализация очень медленная: если вы можете попробовать использовать GPGPU для этого. Я не мог использовать GPGPU (XNA), поэтому я сделал следующее:
    • Настройте сетку, в которой первые N / 2 столбцы представляли собой N / 2 квадратов с ТОЧНОЙ позицией ТОЧНО (охватывающей первый столбец конечного буфера), но с разными координатами текстуры (то же самое для вторых N / 2 столбцов)
    • Отключите тестирование глубины на GPU.
    • Используйте функцию смешивания пикселей MIN.
  4. Do (6) и (7).

Это довольно изобретательно: это в основном прямой перевод того, как тени обрабатываются в 3D в 2D.

Ловушки

Основная проблема заключается в том, что некоторые объекты не должны быть затенены: в моем примере я писал клон Liero (черви в реальном времени) и поэтому не хотел, например, чтобы черви игроков были затенены (по крайней мере один на экране каждого игрока). Все, что я сделал для этих «специальных» объектов, это перерисовал их в качестве последнего шага. Ирония заключалась в том, что большинство объектов не были затенены (черви, пейзаж на переднем плане), поэтому здесь возникает проблема с перерисовкой.

Джонатан Дикинсон
источник
Была ли ваша настройка на метод изменения размера единственной вещью, чтобы ускорить его для обработки 500 источников света выше 60 кадров в секунду?
TravisG
Хорошо, я приму ответ, так как он решает мою первоначальную проблему, но на самом деле он не отвечает тому, за что я дал награду. Я подожду, и, может быть, кто-то придет долго, чтобы объяснить один из нескольких методов O (N) для генерации поля со знаком со знаком.
TravisG
@heishe относительно вашего первого вопроса: не уверен. Я выполнил все оптимизации за один проход - хотя я помню, что выключил его и наблюдал, как существенно снижается частота кадров. Всего 6 дро-вызовов за свет убьют вашу частоту кадров. Как я уже сказал, похоже, что на шаге (5) у вас есть 4 поля расстояния со знаком, но кто-то, кто знает о них больше, должен будет это подтвердить.
Джонатан Дикинсон
Ну, это очень особый случай поля со знаком. В нормальном поле расстояния со знаком каждый пиксель содержит расстояние до ближайшего препятствия. В этом алгоритме поле расстояния содержит только одно препятствие, а также препятствие составляет всего 1 пиксель во всем изображении (источник света), поэтому это поле расстояния может быть сгенерировано за O (N).
TravisG
1
@heishe вот мой шейдер: gist.github.com/2384073 . DistortPS составляет 2 + 3.
Джонатан Дикинсон