Как правило, есть два способа борьбы с этим. В наши дни они называются рендерингом с отложенным и отложенным рендерингом. Существует один вариант этих двух, который я расскажу ниже.
Прямой рендеринг
Визуализируйте каждый объект один раз для каждого источника света, который на него влияет. Это включает в себя окружающий свет. Вы используете аддитивный режим наложения ( glBlendFunc(GL_ONE, GL_ONE)
), поэтому вклады каждого источника света добавляются друг к другу. Поскольку вклад различных источников света является аддитивным, кадровый буфер в конечном итоге получает значение
Вы можете получить HDR путем рендеринга в кадровый буфер с плавающей точкой. Затем вы делаете последний проход по сцене, чтобы уменьшить значения освещения HDR до видимого диапазона; это также будет тем, где вы реализуете bloom и другие пост-эффекты.
Обычное улучшение производительности для этой техники (если в сцене много объектов) заключается в использовании «предварительного прохождения», когда вы визуализируете все объекты, не отрисовывая ничего в цветовом кадровом буфере (используйте glColorMask
для отключения записи цвета). Это просто заполняет буфер глубины. Таким образом, если вы визуализируете объект, который находится позади другого, графический процессор может быстро пропустить эти фрагменты. Он все еще должен запускать вершинный шейдер, но он может пропустить обычно более дорогие вычисления фрагментного шейдера.
Это проще для кода и проще для визуализации. А на некоторых аппаратных средствах (в основном мобильных и встроенных графических процессорах) он может быть более эффективным, чем альтернативный. Но на более высоком оборудовании альтернатива обычно выигрывает в сценах с большим количеством источников света.
Отложенный рендеринг
Отложенный рендеринг немного сложнее.
Уравнение освещения, которое вы используете для расчета света для точки на поверхности, использует следующие параметры поверхности:
- Положение поверхности
- Поверхностные нормали
- Поверхность диффузного цвета
- Поверхность зеркального цвета
- Поверхностное зеркальное сияние
- Возможно, другие параметры поверхности (в зависимости от сложности вашего уравнения освещения)
При прямом рендеринге эти параметры попадают в функцию освещения фрагментного шейдера либо путем передачи непосредственно из вершинного шейдера, либо извлечения из текстур (обычно через координаты текстуры, передаваемые из вершинного шейдера), либо путем генерации из цельного полотна в фрагментном шейдере на основе другие параметры. Рассеянный цвет может быть вычислен путем объединения цвета для каждой вершины с текстурой, комбинирования нескольких текстур, что угодно.
В отложенном рендеринге мы делаем все это явным. На первом проходе мы визуализируем все объекты. Но мы не делаем цвета . Вместо этого мы визуализируем параметры поверхности . Таким образом, каждый пиксель на экране имеет набор параметров поверхности. Это делается с помощью рендеринга на внеэкранные текстуры. Одна текстура будет хранить диффузный цвет как его RGB и, возможно, зеркальный блеск как альфа. Другая текстура будет хранить зеркальный цвет. Третий хранит нормальный. И так далее.
Позиция обычно не сохраняется. Вместо этого он восстанавливается во втором проходе по математике, которая слишком сложна, чтобы попасть сюда. Достаточно сказать, что мы используем буфер глубины и положение фрагмента экранного пространства в качестве входных данных, чтобы выяснить положение камеры в пространстве на поверхности.
Итак, теперь, когда эти текстуры содержат практически всю информацию о поверхности для каждого видимого пикселя в сцене, мы начинаем рендерить полноэкранные четырехугольники. Каждый свет получает полноэкранный четырехъядерный рендер. Мы производим выборку из текстур параметров поверхности (и восстанавливаем положение), а затем просто используем их для вычисления вклада этого света. Это добавлено (снова glBlendFunc(GL_ONE, GL_ONE)
) к изображению. Мы продолжаем делать это, пока у нас не кончится свет.
HDR снова является шагом после обработки.
Самым большим недостатком отложенного рендеринга является сглаживание. Это требует немного больше работы для сглаживания должным образом.
Самый большой плюс, если у вашего GPU много пропускной способности памяти, это производительность. Фактическую геометрию мы визуализируем только один раз (или 1 + 1 на свет, у которого есть тени, если мы делаем отображение теней). Мы никогда не тратим время на скрытые пиксели или геометрию, которые не видны после этого. Все время прохождения освещения тратится на то, что действительно видно.
Если ваш GPU не имеет большой пропускной способности памяти, то световой поток действительно может пострадать. Вытягивание 3-5 текстур на пиксель экрана - это не весело.
Легкий предварительный проход
Это своего рода вариант отложенного рендеринга, который имеет интересные компромиссы.
Как и при отложенном рендеринге, вы отображаете параметры вашей поверхности в виде набора буферов. Тем не менее, у вас есть сокращенные данные поверхности; единственные данные о поверхности, которые вас интересуют в это время, - это значение буфера глубины (для восстановления положения), нормаль и зеркальная блеск.
Затем для каждого источника света вы вычисляете только результаты освещения. Нет умножения с цветами поверхности, ничего. Только точка (N, L) и зеркальный термин, совершенно без цвета поверхности. Зеркальные и диффузные термины должны храниться в отдельных буферах. Зеркальные и рассеянные термины для каждого источника суммируются в двух буферах.
Затем вы заново визуализируете геометрию, используя расчеты общего зеркального и рассеянного освещения, чтобы получить окончательную комбинацию с цветом поверхности, таким образом получая общую отражательную способность.
Плюсы здесь в том, что вы получаете мультисэмплинг обратно (по крайней мере, проще, чем с отсрочкой). Вы делаете меньше для каждого объекта рендеринга, чем прямой рендеринг. Но главное, что это обеспечивает, - это более легкое время для разных уравнений освещения для разных поверхностей.
При отложенном рендеринге вы обычно рисуете всю сцену с одним и тем же шейдером для каждого источника света. Поэтому каждый объект должен использовать одинаковые параметры материала. С предварительным проходом света вы можете назначить каждому объекту свой шейдер, чтобы он мог самостоятельно выполнить последний шаг освещения.
Это не дает столько свободы, как в случае с рендерингом вперед. Но это все еще быстрее, если у вас есть пропускная способность текстуры.