Есть ли способ использовать произвольное количество источников света в фрагментном шейдере?

19

Есть ли способ передать произвольное количество источников света (и цветов) для шейдера фрагмента и зациклить их в шейдере?

Если нет, то как предполагается моделировать несколько источников света? Например, что касается рассеянного направленного освещения, вы не можете просто передать сумму световых весов для шейдера.

NotRoyal
источник
Я не работал с WebGL, но в OpenGL у вас есть максимум 8 источников света. На мой взгляд, если вы хотите передать больше, вы должны использовать, например, однородные переменные.
zacharmarz
Старый метод состоял в том, чтобы всегда пропускать все источники света, неиспользуемые источники света были установлены на 0 яркости и, следовательно, не влияли на сцену. Вероятно, больше не используется ;-)
Патрик Хьюз
7
Когда вы делаете подобные вещи в Google, не используйте термин «WebGL» - технология слишком молода для людей, чтобы хотя бы подходить к этим проблемам. Возьмите этот поиск, например, «Мне повезло», сработало бы. Помните, что проблема WebGL должна красиво переводиться на ту же проблему OpenGL.
Джонатан Дикинсон
Для более чем 8 источников света при прямом рендеринге я обычно использую многопроходный шейдер и назначаю каждому проходу отдельную группу из 8 источников света для обработки с использованием аддитивного смешивания.
ChrisC

Ответы:

29

Как правило, есть два способа борьбы с этим. В наши дни они называются рендерингом с отложенным и отложенным рендерингом. Существует один вариант этих двух, который я расскажу ниже.

Прямой рендеринг

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

Вы можете получить HDR путем рендеринга в кадровый буфер с плавающей точкой. Затем вы делаете последний проход по сцене, чтобы уменьшить значения освещения HDR до видимого диапазона; это также будет тем, где вы реализуете bloom и другие пост-эффекты.

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

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

Отложенный рендеринг

Отложенный рендеринг немного сложнее.

Уравнение освещения, которое вы используете для расчета света для точки на поверхности, использует следующие параметры поверхности:

  • Положение поверхности
  • Поверхностные нормали
  • Поверхность диффузного цвета
  • Поверхность зеркального цвета
  • Поверхностное зеркальное сияние
  • Возможно, другие параметры поверхности (в зависимости от сложности вашего уравнения освещения)

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

В отложенном рендеринге мы делаем все это явным. На первом проходе мы визуализируем все объекты. Но мы не делаем цвета . Вместо этого мы визуализируем параметры поверхности . Таким образом, каждый пиксель на экране имеет набор параметров поверхности. Это делается с помощью рендеринга на внеэкранные текстуры. Одна текстура будет хранить диффузный цвет как его RGB и, возможно, зеркальный блеск как альфа. Другая текстура будет хранить зеркальный цвет. Третий хранит нормальный. И так далее.

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

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

HDR снова является шагом после обработки.

Самым большим недостатком отложенного рендеринга является сглаживание. Это требует немного больше работы для сглаживания должным образом.

Самый большой плюс, если у вашего GPU много пропускной способности памяти, это производительность. Фактическую геометрию мы визуализируем только один раз (или 1 + 1 на свет, у которого есть тени, если мы делаем отображение теней). Мы никогда не тратим время на скрытые пиксели или геометрию, которые не видны после этого. Все время прохождения освещения тратится на то, что действительно видно.

Если ваш GPU не имеет большой пропускной способности памяти, то световой поток действительно может пострадать. Вытягивание 3-5 текстур на пиксель экрана - это не весело.

Легкий предварительный проход

Это своего рода вариант отложенного рендеринга, который имеет интересные компромиссы.

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

Затем для каждого источника света вы вычисляете только результаты освещения. Нет умножения с цветами поверхности, ничего. Только точка (N, L) и зеркальный термин, совершенно без цвета поверхности. Зеркальные и диффузные термины должны храниться в отдельных буферах. Зеркальные и рассеянные термины для каждого источника суммируются в двух буферах.

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

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

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

Это не дает столько свободы, как в случае с рендерингом вперед. Но это все еще быстрее, если у вас есть пропускная способность текстуры.

Николь Болас
источник
-1: не упомянуть LPP / PPL. -1 отложено: рендеринг - это мгновенный выигрыш на любом оборудовании DX9.0 (да, даже на моем «деловом» ноутбуке) - что является базовыми требованиями примерно в 2009 году. Если вы не ориентируетесь на DX8.0 (который не может выполнять отложенный / LPP) Отложено / LPP по умолчанию . Наконец, «большая пропускная способность памяти» безумна - мы, как правило, даже не насыщаем PCI-X x4, тем не менее, LPP существенно снижает пропускную способность памяти. Наконец, -1 за ваш комментарий; петли, как это хорошо? Вы знаете, что эти циклы происходят 2073600 раз за кадр, верно? Даже с паррелизмом видеокарты это плохо.
Джонатан Дикинсон
1
@JonathanDickinson Я думаю, что он имел в виду, что пропускная способность памяти для отложенного / легкого предварительного прохождения обычно в несколько раз больше, чем для прямого рендеринга. Это не лишает законной силы отложенный подход; это просто то, что нужно учитывать при выборе. Кстати, ваши отложенные буферы должны быть в видеопамяти, поэтому пропускная способность PCI-X не имеет значения; важна внутренняя пропускная способность графического процессора. Длинные пиксельные шейдеры, например, с развернутым циклом, не о чем волноваться, если они выполняют полезную работу. И нет ничего плохого в приеме препасса в z-буфере; это работает отлично.
Натан Рид
3
@JonathanDickinson: речь идет о WebGL, поэтому любое обсуждение «моделей шейдеров» не имеет значения. И какой тип рендеринга использовать не является «религиозной темой»: это просто вопрос того, на каком оборудовании вы работаете. Встроенный графический процессор, где «видеопамять» - это обычная память процессора, очень плохо работает с отложенным рендерингом. На мобильном рендерере на основе плиток это еще хуже . Отложенный рендеринг не является «мгновенным выигрышем» независимо от аппаратного обеспечения; у него есть свои компромиссы, как и у любого оборудования.
Николь Болас
2
@JonathanDickinson: «Кроме того, с помощью трюка предварительного прохождения z-буфера вы будете бороться за устранение z-борьбы с объектами, которые должны быть нарисованы». Это полная чушь. Вы визуализируете одни и те же объекты с одинаковыми матрицами преобразования и одним и тем же вершинным шейдером. Многопроходный рендеринг был сделан в Voodoo за 1 день; это решенная проблема. Накопление освещения ничего не меняет.
Николь Болас
8
@JonathanDickinson: Но мы не говорим о рендеринге каркаса, не так ли? Мы говорим о рендеринге тех же треугольников, что и раньше. OpenGL гарантирует неизменность для того же объекта, который отображается (конечно, если вы используете один и тот же вершинный шейдер, и даже тогда, есть invariantключевое слово, чтобы гарантировать его для других случаев).
Никол Болас
4

Вам нужно использовать отложенный рендеринг или предварительное освещение . Некоторые из старых конвейеров с фиксированной функцией (читай: без шейдеров) поддерживают до 16 или 24 источников света - но это все . Отложенный рендеринг устраняет ограничение света; но за счет гораздо более сложной системы рендеринга.

Очевидно, WebGL поддерживает MRT, который абсолютно необходим для любой формы отложенного рендеринга, поэтому это может быть выполнимо; Я просто не уверен, насколько это правдоподобно.

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

Еще один простой способ справиться с этим - просто расставить приоритеты для источников света (возможно, исходя из расстояния до игрока и того, находятся ли они в усеченной камере) и включить только верхние 8. Многие титулы ААА смогли сделать это без особого влияния по качеству продукции (например, Far Cry 1).

Вы также можете посмотреть в заранее рассчитанные карты освещения . Такие игры, как Quake 1, получили большое преимущество от них - и они могут быть довольно маленькими (билинейная фильтрация довольно хорошо смягчает растянутые световые карты). К сожалению, предварительный расчет исключает понятие 100% динамического освещения, но он действительно выглядит великолепно . Вы можете объединить это с вашим пределом в 8 источников света, так что, например, только ракеты или тому подобное будут иметь настоящий источник света - но источники света на стене или что-то подобное будут картами освещения.

Примечание: ты не хочешь зациклить их в шейдере? Попрощайся со своим выступлением. Графический процессор не является процессором и не предназначен для работы так же, как, например, JavaScript. Помните, что каждый пиксель, который вы отрисовываете (даже если он перезаписывается), должен выполнять цикл - поэтому, если вы берете работу с разрешением 1920x1080 и простой цикл, который выполняется 16 раз, вы фактически выполняете все внутри этого цикла 33177600 раз. Ваша видеокарта будет выполнять множество этих фрагментов параллельно, но эти циклы будут по-прежнему использовать старое оборудование.

Джонатан Дикинсон
источник
-1: «Вам нужно использовать отложенный рендеринг». Это совсем не так. Отложенный рендеринг, безусловно, способ сделать это, но это не единственный способ. Также циклы не так уж плохи с точки зрения производительности, особенно если они основаны на одинаковых значениях (то есть: каждый фрагмент не имеет разной длины цикла).
Николь Болас
1
Пожалуйста, прочитайте 4-й абзац.
Джонатан Дикинсон
2

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

Это основная идея. Конечно, есть много оптимизаций, необходимых для того, чтобы сделать это достаточно быстро для сцены разумного размера. Не рисуйте все источники света, только видимые (отбраковка усеченного конуса и окклюзии); фактически не перерисовывайте всю сцену каждый проход, только объекты в пределах диапазона света в этом проходе; У вас есть несколько версий шейдера, которые поддерживают разное количество источников света (1, 2, 3, ...), поэтому вы не тратите время на оценку большего количества источников света, чем нужно.

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

Натан Рид
источник