Итак, в общем, вы рассчитываете
f(x) = a^gamma + b^gamma + ...
Однако, что вы действительно хотите (и, как уже отмечалось @NicolBolas в комментариях), это
g(x) = (a + b + ...)^gamma
Теперьf(x)
и g(x)
будут равняться друг другу только в довольно бесполезных случаях, таких как gamma=1
.Вы просто не можете аддитивно разложить нелинейную функцию, такую как мощность, таким образом.
Правильное решение состоит в том, чтобы смешать все вместе в линейном пространстве и затем выполнить гамма-коррекцию на общую сумму линейных вкладов каждого источника света..
Однако реализация этого приведет к нескольким техническим проблемам.Во-первых, стандартные 8 бит на канал просто недостаточно точны для хранения линейных значений цвета.Использование такого формата для этапа накопления приведет к появлению ярко видимых артефактов цветовых полос.Есть два подхода для решения этой проблемы:
Использовать более высокий бит-канальный формат для накопительного кадрового буфера.Вам понадобится отдельный проход гамма-коррекции, поэтому вам нужно настроить визуализацию на текстуру через FBO.GL_RGBA16F
представляется особенно хорошим форматом для этого.Поскольку вы используете модель освещения PBR, вы также можете работать со значениями цвета за пределами [0,1], и вместо простой гамма-коррекции применить правильное отображение тонов в последнем проходе.Обратите внимание, что, хотя вам может не понадобиться альфа-канал, все равно используйте здесь формат RGBA
, но форматы RGB
просто не требуются для форматов цветовых буферов согласно спецификации GL, поэтому они могут не поддерживаться универсально.
Сохранение данных в формате 8 бит на компонент с гамма-коррекцией.Ключевым моментом здесь является то, что смешивание все еще должно выполняться в линейном пространстве, поэтому значения цвета целевого кадрового буфера должны быть линеаризованы до смешивания.Это может быть достигнуто с помощью кадрового буфера с форматом GL_SRGB8_ALPHA8
и включением GL_FRAMEBUFFER_SRGB
.В этом случае графический процессор автоматически применяет стандартную гамма-коррекцию sRGB при записи цвета фрагмента в кадровый буфер (что в настоящее время делает ваш фрагментный шейдер), но это также приведет к линеаризации sRGB при доступе к этим значениям, в том числе для смешивания.Спецификация профиля ядра ядра в разделе «17.3.6.1 Уравнение смешивания»:
Если включен FRAMEBUFFER_SRGB
и значение FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
для вложения кадрового буфера соответствуетв буфер назначения SRGB
(см. раздел 9.2.3), значения цвета назначения R
, G
и B
(после преобразования с фиксированной точкой в плавающую) считаются закодированными дляцветовое пространство sRGB и, следовательно, должно быть линеаризовано до их использования при смешивании.Каждый компонент R
, G
и B
преобразуется так же, как описано для компонентов текстуры sRGB в разделе 8.24.
Подход 1 будет намногоболее общий подход, в то время как подход 2 имеет несколько недостатков:
- , линеаризация / делинеризация выполняется несколько раз, что может привести к потере некоторой вычислительной мощности графического процессора
- из-за использования всего лишь 8 битцелое число, общее качество будет ниже.После каждого шага наложения результаты округляются до следующего представимого числа, поэтому вы получите намного больший шум квантования.
- вы все еще ограничены значениями цвета в [0,1] и не можете (легко) делать большеинтересные эффекты тонального отображения и рендеринга HDR
Однако, подход 2 также имеет преимущества:
- вам не нужен отдельный финальный проход гамма-коррекции
- , есливаша платформа / оконная система поддерживает фрейм-буферы sRGB, вы можете напрямую создавать пиксельный формат / визуал sRGB для своего окна и вообще не требовать какого-либо шага рендеринга в текстуру.По сути, для этого достаточно запроса кадрового буфера sRGB и включения
GL_FRAMEBUFFER_SRGB
.