Есть ли способ передать произвольное количество источников света (и цветов) для шейдера фрагмента и зациклить их в шейдере?
Если нет, то как предполагается моделировать несколько источников света? Например, что касается рассеянного направленного освещения, вы не можете просто передать сумму световых весов для шейдера.
Ответы:
Как правило, есть два способа борьбы с этим. В наши дни они называются рендерингом с отложенным и отложенным рендерингом. Существует один вариант этих двух, который я расскажу ниже.
Прямой рендеринг
Визуализируйте каждый объект один раз для каждого источника света, который на него влияет. Это включает в себя окружающий свет. Вы используете аддитивный режим наложения (
glBlendFunc(GL_ONE, GL_ONE)
), поэтому вклады каждого источника света добавляются друг к другу. Поскольку вклад различных источников света является аддитивным, кадровый буфер в конечном итоге получает значениеВы можете получить HDR путем рендеринга в кадровый буфер с плавающей точкой. Затем вы делаете последний проход по сцене, чтобы уменьшить значения освещения HDR до видимого диапазона; это также будет тем, где вы реализуете bloom и другие пост-эффекты.
Обычное улучшение производительности для этой техники (если в сцене много объектов) заключается в использовании «предварительного прохождения», когда вы визуализируете все объекты, не отрисовывая ничего в цветовом кадровом буфере (используйте
glColorMask
для отключения записи цвета). Это просто заполняет буфер глубины. Таким образом, если вы визуализируете объект, который находится позади другого, графический процессор может быстро пропустить эти фрагменты. Он все еще должен запускать вершинный шейдер, но он может пропустить обычно более дорогие вычисления фрагментного шейдера.Это проще для кода и проще для визуализации. А на некоторых аппаратных средствах (в основном мобильных и встроенных графических процессорах) он может быть более эффективным, чем альтернативный. Но на более высоком оборудовании альтернатива обычно выигрывает в сценах с большим количеством источников света.
Отложенный рендеринг
Отложенный рендеринг немного сложнее.
Уравнение освещения, которое вы используете для расчета света для точки на поверхности, использует следующие параметры поверхности:
При прямом рендеринге эти параметры попадают в функцию освещения фрагментного шейдера либо путем передачи непосредственно из вершинного шейдера, либо извлечения из текстур (обычно через координаты текстуры, передаваемые из вершинного шейдера), либо путем генерации из цельного полотна в фрагментном шейдере на основе другие параметры. Рассеянный цвет может быть вычислен путем объединения цвета для каждой вершины с текстурой, комбинирования нескольких текстур, что угодно.
В отложенном рендеринге мы делаем все это явным. На первом проходе мы визуализируем все объекты. Но мы не делаем цвета . Вместо этого мы визуализируем параметры поверхности . Таким образом, каждый пиксель на экране имеет набор параметров поверхности. Это делается с помощью рендеринга на внеэкранные текстуры. Одна текстура будет хранить диффузный цвет как его RGB и, возможно, зеркальный блеск как альфа. Другая текстура будет хранить зеркальный цвет. Третий хранит нормальный. И так далее.
Позиция обычно не сохраняется. Вместо этого он восстанавливается во втором проходе по математике, которая слишком сложна, чтобы попасть сюда. Достаточно сказать, что мы используем буфер глубины и положение фрагмента экранного пространства в качестве входных данных, чтобы выяснить положение камеры в пространстве на поверхности.
Итак, теперь, когда эти текстуры содержат практически всю информацию о поверхности для каждого видимого пикселя в сцене, мы начинаем рендерить полноэкранные четырехугольники. Каждый свет получает полноэкранный четырехъядерный рендер. Мы производим выборку из текстур параметров поверхности (и восстанавливаем положение), а затем просто используем их для вычисления вклада этого света. Это добавлено (снова
glBlendFunc(GL_ONE, GL_ONE)
) к изображению. Мы продолжаем делать это, пока у нас не кончится свет.HDR снова является шагом после обработки.
Самым большим недостатком отложенного рендеринга является сглаживание. Это требует немного больше работы для сглаживания должным образом.
Самый большой плюс, если у вашего GPU много пропускной способности памяти, это производительность. Фактическую геометрию мы визуализируем только один раз (или 1 + 1 на свет, у которого есть тени, если мы делаем отображение теней). Мы никогда не тратим время на скрытые пиксели или геометрию, которые не видны после этого. Все время прохождения освещения тратится на то, что действительно видно.
Если ваш GPU не имеет большой пропускной способности памяти, то световой поток действительно может пострадать. Вытягивание 3-5 текстур на пиксель экрана - это не весело.
Легкий предварительный проход
Это своего рода вариант отложенного рендеринга, который имеет интересные компромиссы.
Как и при отложенном рендеринге, вы отображаете параметры вашей поверхности в виде набора буферов. Тем не менее, у вас есть сокращенные данные поверхности; единственные данные о поверхности, которые вас интересуют в это время, - это значение буфера глубины (для восстановления положения), нормаль и зеркальная блеск.
Затем для каждого источника света вы вычисляете только результаты освещения. Нет умножения с цветами поверхности, ничего. Только точка (N, L) и зеркальный термин, совершенно без цвета поверхности. Зеркальные и диффузные термины должны храниться в отдельных буферах. Зеркальные и рассеянные термины для каждого источника суммируются в двух буферах.
Затем вы заново визуализируете геометрию, используя расчеты общего зеркального и рассеянного освещения, чтобы получить окончательную комбинацию с цветом поверхности, таким образом получая общую отражательную способность.
Плюсы здесь в том, что вы получаете мультисэмплинг обратно (по крайней мере, проще, чем с отсрочкой). Вы делаете меньше для каждого объекта рендеринга, чем прямой рендеринг. Но главное, что это обеспечивает, - это более легкое время для разных уравнений освещения для разных поверхностей.
При отложенном рендеринге вы обычно рисуете всю сцену с одним и тем же шейдером для каждого источника света. Поэтому каждый объект должен использовать одинаковые параметры материала. С предварительным проходом света вы можете назначить каждому объекту свой шейдер, чтобы он мог самостоятельно выполнить последний шаг освещения.
Это не дает столько свободы, как в случае с рендерингом вперед. Но это все еще быстрее, если у вас есть пропускная способность текстуры.
источник
invariant
ключевое слово, чтобы гарантировать его для других случаев).Вам нужно использовать отложенный рендеринг или предварительное освещение . Некоторые из старых конвейеров с фиксированной функцией (читай: без шейдеров) поддерживают до 16 или 24 источников света - но это все . Отложенный рендеринг устраняет ограничение света; но за счет гораздо более сложной системы рендеринга.
Очевидно, WebGL поддерживает MRT, который абсолютно необходим для любой формы отложенного рендеринга, поэтому это может быть выполнимо; Я просто не уверен, насколько это правдоподобно.
В качестве альтернативы вы можете исследовать Unity 5, которая отложила рендеринг прямо из коробки.
Еще один простой способ справиться с этим - просто расставить приоритеты для источников света (возможно, исходя из расстояния до игрока и того, находятся ли они в усеченной камере) и включить только верхние 8. Многие титулы ААА смогли сделать это без особого влияния по качеству продукции (например, Far Cry 1).
Вы также можете посмотреть в заранее рассчитанные карты освещения . Такие игры, как Quake 1, получили большое преимущество от них - и они могут быть довольно маленькими (билинейная фильтрация довольно хорошо смягчает растянутые световые карты). К сожалению, предварительный расчет исключает понятие 100% динамического освещения, но он действительно выглядит великолепно . Вы можете объединить это с вашим пределом в 8 источников света, так что, например, только ракеты или тому подобное будут иметь настоящий источник света - но источники света на стене или что-то подобное будут картами освещения.
Примечание: ты не хочешь зациклить их в шейдере? Попрощайся со своим выступлением. Графический процессор не является процессором и не предназначен для работы так же, как, например, JavaScript. Помните, что каждый пиксель, который вы отрисовываете (даже если он перезаписывается), должен выполнять цикл - поэтому, если вы берете работу с разрешением 1920x1080 и простой цикл, который выполняется 16 раз, вы фактически выполняете все внутри этого цикла 33177600 раз. Ваша видеокарта будет выполнять множество этих фрагментов параллельно, но эти циклы будут по-прежнему использовать старое оборудование.
источник
Вы можете использовать пиксельный шейдер, который поддерживает n источников света (где n - это небольшое число, например 4 или 8), и перерисовывать сцену несколько раз, каждый раз пропуская новую серию источников света и используя аддитивное смешивание, чтобы объединить их все вместе.
Это основная идея. Конечно, есть много оптимизаций, необходимых для того, чтобы сделать это достаточно быстро для сцены разумного размера. Не рисуйте все источники света, только видимые (отбраковка усеченного конуса и окклюзии); фактически не перерисовывайте всю сцену каждый проход, только объекты в пределах диапазона света в этом проходе; У вас есть несколько версий шейдера, которые поддерживают разное количество источников света (1, 2, 3, ...), поэтому вы не тратите время на оценку большего количества источников света, чем нужно.
Отложенный рендеринг, как упомянуто в другом ответе, является хорошим выбором, когда у вас много маленьких источников света, но это не единственный способ.
источник