Кластерное освещение
Огни являются отличным способом добавить реализм в ваше приложение. Однако реальное время освещения также может создать значительные затраты на производительность во время выполнения, особенно когда у вас большое количество источников света, которые создают тени.
Часть решения для снижения затрат на производительность может включать ограничение количества источников света, которые влияют на отдельные сетки. Это часто реализуется путем поиска и использования источников света, которые находятся рядом с каждым объектом. Однако у этой стратегии есть несколько недостатков:
- Поскольку каждый объект может использовать разные наборы источников света, необходимо скомпилировать пользовательский шейдер для их обработки.
- Большие объекты необходимо разделить на меньшие объекты, чтобы эта стратегия была эффективной.
- Большое количество источников света, создающих тени, может вызвать исчерпание доступных слотов текстур, используемых картами теней, в шейдере.
Чтобы решить эти проблемы, PlayCanvas использует решение Clustered Lighting для обеспечения производительной реализации Omni Lights и Spot Lights. Он хранит информацию об источниках света в текстурах и позволяет GPU легко использовать только те источники света, которые находятся рядом с затененным фрагментом. У кластерного освещения есть несколько преимуществ:
- Шейдерам не нужно перекомпилироваться при добавлении или удалении источников света из сцены, поскольку шейдер может обрабатывать несколько источников света.
- Большое количество источников света (включая тени и куки) может использоваться в сцене, поскольку оцениваются только источники света, находящиеся рядом с каждым пикселем.
Обратите внимание, что направленные источники света влияют на все объекты, поэтому они не используют решение кластерного освещения.
Обзор реализации
Следующие шаги дают основное представление о реализации кластерного освещения:
- Отсечь источники света по всем фрустумам камеры, чтобы оценить список видимых источников света для кадра.
- Разместить 3D-сетку в мировом пространстве над осями, выровненными по границам всех видимых источников света.
- Каждая ячейка в 3D-сетке хранит индексы источников света, которые пересекаются с ней. На ЦП информация обновляется каждый кадр, и ей разрешается получать список источников света, которые влияют на любые позиции. Информация хранится в текстуре и доступна для GPU.
- Свойства всех видимых источников света хранятся в другой текстуре, чтобы они также были доступны для GPU.
- Теневые карты и текстуры куки отображаются в атласе, а не в отдельных текстурах, чтобы все они были доступны шейдеру одновременно.
- Во время оценки освещения во фрагментном шейдере позиция фрагмента в мировом пространстве используется для доступа к ячейке 3D-сетки и оценки хранимых источников света.
Опции редактора
Опции для кластерного освещения можно найти в настройках редактора в разделе «Rendering».
Это позволит вам отключить кластерное освещение (если вам нужно использовать предыдущую систему освещения) и настроить производительность и функции, упомянутые ниже.
Настройка кластерного освещения
Включение функций
Шейдер кластерного освещения должен обрабатывать все поддерживаемые источники света, поэтому он должен содержать код для обработки этих функций. Это может вызвать увеличение размера шейдера и замедление компиляции. Чтобы решить эти проблемы, существует набор опций функций, которые позволяют отключить функции, не нужные вашему приложению, и ускорить компиляцию шейдера:
- Shadows Enabled – Включить или отключить поддержку теней
- Cookies Enabled – Включить или отключить поддержку куки для источников света
- Area Lights Enables – Включить или отключить поддержку площадных источников света
Настройка 3D-сетки
Свойство Cells позволяет указать количество ячеек вдоль каждой оси мирового пространства. Это динамически делит ось, выровненную по ограничивающему прямоугольнику, содержащему все видимые источники света, на указанное количество ячеек.
Свойство Max Lights Per Cell позволяет указать максимальное количество источников света, хранящихся в каждой отдельной ячейке. Это представляет максимальное количество перекрывающихся источников света. Обычно количество источников света должно быть увеличено для более крупных разделений сетки, так как перекрытие света больше.
Настройка атласа
Все карты теней и текстуры Cookie, используемые видимыми источниками света, хранятся в атласе. Существует одна текстура атласа для теней и другая для Cookie. Атласы могут иметь разные разрешения, хотя внутри они используют одно и то же разделение на меньшие области, используемые отдельными источниками света.
Shadow Atlas Resolution позволяет настроить размер атласа теней, в то время как Cookie Atlas Resolution позволяет установить атлас Cookie. Размеры не обязательно должны быть степенью 2.
Atlas Split контролирует, как атлас разделяется на отдельные подтекстуры, используемые источниками света. Существует две стратегии разделения:
- Automatic – Когда размер массива указан как 0, движок автоматически разделяет атлас по мере необходимости, чтобы назначить каждому видимому источнику света равномерно размерную подтекстуру. Например, если у вас есть три видимых источника света в кадре, атлас будет разделен на 2x2 подтекстуры, и три из этих четырех подтекстур будут назначены источникам света.
- Manual – Позволяет разделить атлас на фиксированное количество подтекстур, которые могут быть разных размеров. Он настраивается с использованием массива чисел, где каждое число представляет разделение, как вертикально, так и горизонтально. В следующем разделе приведен пример ручного разделения атласа.
Настройка ручного разделения атласа
Чтобы понять, как атлас разделяется вручную, возьмите массив с двумя числами: [2, 2]. Первое число в массиве разделяет атлас на 2x2, на общую сумму четырех областей. Любые последующие числа в массиве снова разделяют эти области. В данном случае второе число в массиве разделяет одну из существующих областей на еще одну 2x2 (например, четыре области), на общую сумму 7 областей.
Следующее изображение показывает, как следует указывать ручное разделение атласа.
Другие примеры:
- [3, 2] – Первое число разделяет атлас на 3x3 (9 областей). Второе число разделяет одну из этих областей на 2x2 (4 области), на общую сумму 12 областей.
- [4] – Атлас разделен на 4x4 (16 областей).
Основное преимущество использования ручного разделения заключается в уровне детализации, который можно достичь. Вы можете настроить фиксированное количество подтекстур, которые назначаются источникам света в порядке их размера в пространстве экрана. Это позволяет источникам света, которые больше на экране, получать большую область атласа, в то время как меньшие источники света на расстоянии используют меньшую область атласа. Если источников света больше, чем количество доступных областей, наименьшие по размеру источники света на экране не будут создавать тени.
Типы теней
Все источники света, создающие тени, используют один и тот же тип теней. Это позволяет глобально устанавливать мягкость теней и связанные с ними показатели производительности. Поддерживаются варианты PCF1, PCF3 и PCF5. Дополнительную информацию см. на странице Shadows.
Ограничения
Внутри индекс света хранится с использованием 8 бит, поэтому максимальное количество видимых источников света в любом кадре составляет 254 (один индекс зарезервирован). В будущем может быть добавлена дополнительная опция использования 16 бит для хранения индекса и увеличения лимита.
Вопросы производительности
- Разделение ячеек должно быть как можно меньше, так как большие разделения ячеек приводят к большему использованию процессора при заполнении сетки источниками света в каждом кадре. Это должно быть оптимизировано для каждой сцены в зависимости от ее освещенности. В идеале у вас должно быть достаточно ячеек, чтобы ограничить перекрытие источников света и количество источников света в каждой ячейке.
- Max Lights Per Cell должно быть как можно меньше, так как это ограничивает размер текстуры, используемой для хранения 3D-сетки, которая должна быть обновлена в каждом кадре.
- Если приложение с использованием Clustered Lighting работает медленно на старых мобильных устройствах, рассмотрите возможность глобального отключения функций, таких как тени или Cookie.
Отображение сетки отладки
Для облегчения отладки и настройки производительности с использованием кластерного освещения вы можете назначить Layer ID для рендеринга на debugLayer of LightingParams. например
// Предполагая, что находимся в типе сценария
this.app.scene.lighting.debugLayer = this.app.scene.layers.getLayerByName("World").id;
И чтобы остановить рендеринг, назначьте значение undefined
для свойства debugLayer
:
// Предполагая, что находимся в типе сценария
this.app.scene.lighting.debugLayer = undefined;