Hald CLUT

В Dagon появилась поддержка 3D-текстур, что позволило реализовать цветокоррекцию с использованием цветовых таблиц формата Hald CLUT. CLUT расшифровывается как color lookup table – таблица поиска цвета: в памяти хранится текстура, в которой стандартным цветам sRGB сопоставлены какие-то другие цвета – вместо оригинальных цветов пикселей на вывод идут значения, прочитанные из 3D-таблицы. Принцип примерно тот же, что использовался в индексированных цветовых режимах, только в данном случае таблица охватывает более широкий диапазон RGB. Чаще всего CLUT используется для имитации характерной «пленочной» цветовой гаммы на цифровых снимках, но ее возможности гораздо шире. В таблице цвета могут быть абсолютно любые – с математической точки зрения, она является функцией, которая переносит цвет из одного пространства в другое. Чем больше таблица, тем точнее ее охват.

Оргинал таблицы в формате Hald CLUT выглядит следующим образом (PNG можно скачать тут):

Если отредактировать это изображение в графическом редакторе – например, изменить яркость, контраст, насыщенность и т. д. – результат будет хранить информацию, необходимую для того, чтобы повторить эти же операции на другом изображении. Единственное условие: цветокоррекция должна выполняться для каждого пикселя параллельно и независимо от остальных. Если фильтр использует оконную свертку и другие алгоритмы, работающие с несколькими пикселями одновременно, то метод с использованием CLUT не будет с ним работать.

Преимущество Hald CLUT состоит в эффективном расположении значений – таблица размером 4096×4096 охватывает весь 24-битный диапазон sRGB (16777216 цветов) и при этом отлично сжимается в PNG. Для хранения таблицы важно использовать lossless-формат, так как сжатие с потерями вносит мелкие искажения в цвета, а в данном случае важно сохранить точность информации.

Еще одна немаловажная фича формата – прямая совместимость с 3D-текстурами OpenGL. Достаточно просто декодировать картинку в буфер RGB и создать из этого буфера текстуру функцией glTexImage3D – никаких промежуточных конвертаций не требуется. Эта текстура затем передается в шейдер постобработки, который выглядит совсем элементарно:

vec3 inputColor = texture(colorBuffer, texCoord).rgb;
vec3 outputColor = texture(lookupTable, inputColor).rgb;

В Dagon поддержка создания 3D-текстуры из двумерного буфера встроена в класс Texture. Нужно загрузить таблицу как ассет ImageAsset, создать текстуру и проинициализировать ее методом createHaldCLUT. Результат передается в стандартный стек постобработки (game.postProcessingRenderer):

ImageAsset aCLUT;

override void beforeLoad()
{
    aCLUT = addImageAsset("data/food.png");
}

override void afterLoad()
{
    Texture clut = New!Texture(assetManager);
    clut.createHaldCLUT(aCLUT.image, 256);
    game.postProcessingRenderer.colorLookupTable = clut;
    game.postProcessingRenderer.lutEnabled = true;
}

Поддерживаются таблицы любых разрешений, но вы должны сами правильно вычислить размер 3D-текстуры, соответствующей вашей CLUT. Например, для таблицы 4096×4096 это будет 256x256x256, как в моем примере. Если в этот параметр передать неправильное значение, то будет построена некорректная текстура (в релизе обязательно добавлю валидацию).

Пример использования на основе демки с автомобильной физикой – обработанное изображение и соответствующая таблица:

Физика автомобиля

Демка с физикой автомобиля, которую я разработал в начале этого года для Patreon Sponsor Folder, теперь доступна на GitHub в репозитории https://github.com/gecko0307/vehicle-demo – с многочисленными улучшениями, которые я сделал за последние месяцы (в частности, появилась поддержка модели трения колес Pacejka Magic Formula). Пруф того, что на основе связки Dagon + Newton вполне можно написать гоночную игру. Как-нибудь, возможно, напишу подробную статью на эту тему.

Пример ImGui в Dagon

В Dagon 0.14 появилось расширение для работы с популярным UI-тулкитом ImGui – dagon:imgui. Это у меня уже второй инструмент для создания пользовательских интерфейсов после Nuklear, и во многом ImGui оказался проще и удобнее – хотя, конечно, оба тулкита имеют свои особенности, и нельзя сказать, что один однозначно лучше другого. Но ImGui на сегодняшний день является де-факто стандартом, поэтому его поддержка стала довольно важной вехой в развитии движка. Расширение основано на биндинге BindBC ImGui, модуль bindbc.imgui.ogl (ImGuiOpenGLBackend) оказался полностью совместим с Dagon.

(далее…)

Dagon 0.14.0

Выпустил новую версию движка. Основное нововведение – рефакторинг системы текстур: в частности, текстуры теперь могут быть загружены напрямую, без необходимости создавать объект SuperImage, что позволяет создавать текстуры любого формата, поддерживаемого OpenGL – таким образом, достигается лучшая совместимость с DDS. 2D-текстуры и кубические карты объединены в один класс Texture, и работать с ними стало проще. Появилась поддержка формата сжатия ASTC.

(далее…)

Deferred Texturing для ландшафта

Еще одной новинкой в Dagon 0.14 станет довольно интересная и нетипичная техника, которую я еще не встречал в готовых реализациях – Deferred Texturing (отложенное текстурирование) для ландшафта.

Когда у меня в движке появилась поддержка ландшафтов, я не стал особо мудрить и сделал для них простейший шейдер, к которому подключаются до четырех текстур, причем для них пришлось сделать специальные свойства у материала, что выглядело не очень эстетично с точки зрения дизайна API. Также этот шейдер не поддерживал текстуры шероховатости и металличности, что делало его неполноценным по сравнению с остальными компонентами рендера. Техника Deferred Texturing, описанная Натаном Ридом, практически идеально вписалась в мой пайплайн и полностью решает эти две старые проблемы.

Основная идея заключается в том, чтобы вынести процесс наложения каждой текстуры в отдельный проход. Чтобы не рендерить сетку ландшафта по нескольку раз, мы запекаем геометрические данные в G-буфер, как в обычном отложенном рендере – отличие в том, что нам при этом нужно сохранять только интерполированные вершинные нормали и текстурные координаты ландшафта.

Полученные буферы вместе с PBR-текстурами (карта нормалей, base color, roughness, metallic) скармливаем текстурирующему проходу, который рисует экранный квад и записывает значения уже в обычный G-буфер. Для вычисления финальных нормалей используются значения из буфера нормалей ландшафта (плюс глубина, которую я сохраняю как альфа-канал буфера нормалей – она нужна для реконструирования eyespace-позиции пикселя). PBR-текстуры сэмплируются UV-координатами из буфера. Чтобы экранный квад не перезаписывал значения, уже сохраненные в G-буфере, нужно отсекать пиксель по маске, которую я храню в Z-канале буфера текстурных координат – 0 означает, что в данной точке нет ландшафта, 1 – есть.

Текстурирующий проход срабатывает несколько раз подряд – по количеству слоев текстур, которые я храню в специальном многослойном материале, каждый слой которого сам является отдельным материалом. Проход рисует в режиме альфа-смешивания, альфа-канал для прохода хранится в слое как отдельная текстура. Что самое интересное, прозрачность можно анимировать/клиппить и таким образом создать эффект плавного перехода одной текстуры в другую – например, постепенного покрытия ландшафта снегом. Возможности открываются самые широкие!