Я решил сделать полноценную тему и заострить на ней внимание, так как текстуры — один из важных аспектов в работе, причем довольно много ручной.
Текстуры
Формат текстур .dds — самый простой и легковесный в плане различного рода сжатия с минимальными потерями, кубмаппинга (для отражений и неба) и мипмаппинга (уровни детализации для текстур). Поэтому неудивительно, почему его многие выбирают и выбрали разработчики.
Единственный его минус: данный формат текстур работает только на ПК и консолях, но мне не на телефон портировать.
Но самый главный плюс: Godot Engine отлично ладит с ним, включая быструю загрузку (в будущих версиях 4.7 ещё и поддержку стриминга планируют, что мне и на руку).
Для справки:
Кубическая текстура (Cubemap, Cube Map) — это текстура, состоящая из 6 квадратных изображений, наложенных на грани куба.
Проще говоря: это "обёртка" вокруг сцены из шести картинок, которая даёт иллюзию бесконечного пространства или "реалистичные" отражения.
Мипмаппинг (Mipmapping) — это техника в компьютерной графике, где для одной текстуры заранее создаётся последовательность уменьшенных копий (цепочка мип-уровней).
Простыми словами: это набор "преуменьшенных" версий одной текстуры, чтобы далёкие предметы не мерцали, а рендерились быстро и корректно.
Потоковая передача текстур (Texture Streaming) — это технология динамической загрузки в видеопамять не всей текстуры целиком, а только тех её частей (мип-уровней или тайлов), которые реально нужны в данный момент на экране.
Простыми словами: вместо того чтобы держать в памяти "супертекстуру" 8K целиком, движок подгружает её кусочками по мере того, как вы подходите к стене или поворачиваете камеру.
В игре
Текстуры в игре используются для разных видов элементов с разными настройками:
- Некоторые текстуры из EE-версии сжаты в BC7 с цветовым пространством sRGB и гамма-коррекцией для поддержки "псевдо"-HDR и большого охвата палитры.
- Где важен канал как "дополнительный" альфа или сам альфа-канал с качеством для игровых текстур, используется сжатие BC3 (эффекты, текстуры для моделей, bump, прозрачные текстуры и т.д.).
- Нужен RGB-канал с отличным качеством, но потерей альфа (UI, полупрозрачные текстуры, текстуры с вырезом)? Для этого есть BC2.
- Если нужен только RGB-канал с приемлемым сжатием, то это BC1 (UI без альфы, диффузные текстуры).
Сжатие текстур — это алгоритмическое уменьшение объёма данных текстуры в памяти и на диске с возможностью аппаратного декодирования прямо при рендеринге.
Важно: Современные GPU умеют работать со сжатыми текстурами «напрямую» (форматы BC, ASTC, ETC2), не распаковывая их полностью. Это аппаратное декодирование, которое быстрее, чем софтовое сжатие в оперативной памяти.
- Небо - использует кубмап и мипмапинг.
- Текстура Diffuse для моделей — мипмапинг, изредка ещё и альфа-канал.
- Текстура Bump / Bump# для моделей — мипмапинг, альфа-канал.
- Отражения в воде использует также кубмап, но только внутренний, собранный из отдельных текстур без настроек кубмапа. Для отражения на материалах используется кубмап меньшего размера без мипмапов.
- Для UI-элементов / Эффектов / Прочие — без мипмапов, с альфа-каналом и без него.
- Вместе с основными текстурами также идёт файл настройки текстур в формате .thm. В нём лежат настройки, как если бы текстуры "уже выглядели в самой игре", например: тип сглаживания мипмапов, включение объёмных и детализированных текстур, метод освещения для материала и т.д. Это не особо важно, так как я это опущу в отдельной теме по материалам.
Bump-текстуры
Bump-текстуры (bump maps) — это технология в компьютерной графике, позволяющая имитировать рельеф, царапины, складки и другие неровности на поверхности 3D-объекта без изменения его реальной геометрии.
В самих bump-текстурах хранится не только карта высоты (Height Map) для той самой имитации рельефа, но и с помощью магии упаковки каналов RGBA размещены карта нормалей и их исправление по X / Y / Z (Normal Map) и зеркальная карта (Specular Map). Это некий вид оптимизации, при котором текстуры меньше всего проходят стадию отрисовки, называемую дословно Замена текстур (Texture Swap). Например, видеокарте проще обработать две текстуры с разбитыми каналами, чем шесть, но с каждым отдельно.
Texture Swap: Это самая простая и распространённая операция. Когда GPU отрисовывает объекты, он привязывает к ним определённые текстуры. Как только возникает необходимость отрисовать следующий объект с другой текстурой, происходит "своп" — текущая текстура отключается, а новая привязывается к пайплайну.
Расположение каналов я плохо помню, знаю, что точно не по порядку, и сделано это ради исправления нормалей из-за сжатия.
- Файл с постфиксом _bump - имеет следующий разбор по каналам: Normal X / Y / Z (DirectX формат) и Specular Map.
- Файл с постфиксом _bump# - имеет следующий разбор по каналам: исправление некоторых пикселей у Normal X / Y / Z и Height Map.
Проблем с загрузкой и чтением для Godot Engine нет. Проблемы с оригинальными текстурами отсутствуют, но они начнутся в шейдерах для материала. Если правильно разложить ещё можно каналы и отразить канал Y (Green) у Normal Map для формата OpenGL (с которым Godot и работает), то с исправлением карты Normal будет очень сложно, так как открытой информации я не нашёл, а глубоко копаться в X-Ray Engine не особо охота.
Основное различие между форматами normal map для OpenGL и DirectX кроется в том, по какому правилу интерпретируется зелёный канал (Y-ось) карты нормалей.
OpenGL (Y+): использует правую систему координат (правостороннюю), где ось Y направлена вверх.
DirectX (Y-): использует левую систему координат (левостороннюю), где ось Y направлена вниз.
Решением было заранее разложить каналы на отдельные текстуры с помощью софта "xrModders texture tools" (который исправит почти все косяки), а для оптимизации использовать сжатие текстур, а не по каналам.
Gloss, Height и Normal-текстуры
Текстуры в принципе готовы, осталось их вернуть обратно в .dds формат. Главное — сделать это правильно, а кроме нужного сжатия ещё и цветовое пространство — линейное, ни в коем случае sRGB (это для диффузных текстур).
Диффузная текстура (цвет): sRGB (хранится в гамма-коррекции). Почему? Она создавалась для человеческого глаза. Перед тем как использовать в шейдере, её нужно перевести в линейное пространство, иначе свет будет считаться неправильно (цвета станут слишком тёмными или яркими).
Normal, Specular, Height, HDR, Metallic, Roughness и т.д.: Линейное пространство. Почему? Эти текстуры хранят не цвета, а математические данные (направление вектора, степень блеска, высоту). Их значения должны меняться строго пропорционально, без гамма-коррекции. Если применить к ним sRGB, данные исказятся — нормали начнут вращаться не туда, а высоты и блеск станут нефизичными.
- Gloss — это, грубо говоря, карта Specular. Она должна быть со сжатием BC4, где используется только один канал — красный, что экономит память. В дальнейшем этот тип текстуры нужен для псевдо-PBR эффекта, так как у нас нет Metallic текстуры, а вручную сидеть в Substance Painter я не особо хочу. Придется использовать метод инвертирования через шейдер (просто отразить текстуру в плане цвета):
Metallic = ниже-среднее значение для металлов, ну или 0.0 для неметаллов. Specular = Gloss. Roughness = 1.0 - Gloss. - Height — метод сжатия тот же, что и у Gloss. Данная текстура нужна для имитации рельефа или затемнения (Ambient Occlusion).
- Normal — тут довольно всё просто, сжатие BC5.
При использовании текстуры в качестве карты нормалей требуются только красный и зеленый каналы. Поскольку стандартные алгоритмы сжатия текстур создают артефакты, которые плохо смотрятся на картах нормалей, формат сжатия BC5/RGTC лучше всего подходит для этих данных.
Godot из коробки уже игнорирует синий канал для карт нормалей. Ко всему стандартно применяется ещё мипмаппинг.
Заключение
В прошлой теме, в этой и, возможно, в других я ссылаюсь в моментах на игровой движок 4A Engine и серию игр "Метро 2033". В принципе, неудивительно: разработчики являются бывшими создателями серии "Сталкер". Некоторые ушли, но накопленный опыт, привычки и системы вливаются в дальнейшие работы, а моментов таких полно, включая скрипты и код.
Аналогично и с другими расформированными разработчиками, которые ушли в проект Survarium и написание Vostok Engine.
Я не осуждаю их попытки однообразия; в некоторых моментах они ищут обусловленную простоту для дальнейшей работы, чем я и сам пользуюсь.
На этом, думаю, можно и закончить. Увидимся уже в следующей статье.