KTX2 на практике: правильный подход к сжатию текстур
В предыдущей статье мы разобрали отношения между форматами текстур GPU, Basis Universal и KTX2. Теперь, когда теория ясна, переходим к практике: как выбрать между ETC1S и UASTC, какие инструменты использовать, какие команды вводить и как загружать результат в движках.
Статья готова к копированию — можно читать и сразу применять.
Сначала самое важное: ETC1S или UASTC
Basis Universal предлагает два промежуточных кодирования. Ошибка в выборе — это не просто «недостаточно хорошо», а полное смазывание карт нормалей. Запомните эту таблицу:
| ETC1S | UASTC | |
|---|---|---|
| Степень сжатия | Очень высокая (как JPG) | Средняя (как качественный PNG) |
| Качество | Достаточно для цветных карт | Близко к оригиналу |
| Память GPU (после трансформации) | Обычно 4 бита на пиксель (примерно 1/8 от оригинала) | Обычно 8 бит на пиксель (примерно 1/4 от оригинала) |
| Скорость кодирования | Медленная (можно регулировать) | Быстрая |
| Применение | Albedo/диффузные, emissive | Normal, metalness-roughness, текстурные карты данных |
| Не подходит | Normal, карты с точными числовыми значениями | Цветные карты (из пушки по воробьям — объём больше) |
Почему карты нормалей нельзя использовать с ETC1S? Потому что карты нормалей хранят векторы направления, где каждый пиксель имеет три канала RGB, взаимно ограниченные (длина вектора ≈ 1). ETC1S — это блочное сжатие, оптимизированное для «визуально правильного цвета». Оно нечувствительно к точности отдельных каналов, поэтому после сжатия направление вектора искажается, и освещение сразу становится некорректным — особенно в областях бликов и высокочастотных деталей. UASTC лучше сохраняет числовые значения и выдерживает такие требования к точности.
Практическое правило:
- Цветные карты (albedo, emissive) → ETC1S
- Карты данных (normal, roughness, metallic, AO, толщина) → UASTC
- Если сомневаетесь и хотите сэкономить объём → сначала попробуйте ETC1S, если при приближении видно размытие — переходите на UASTC
Сравнение четырёх форматов на одной текстуре
Возьмём для примера карту albedo размером 2048×2048 (данные — типичные значения из сообщества, ориентировочные):
| Формат | Размер на диске | Память GPU (включая мипмапы) | Загрузка на GPU | Кроссплатформенность |
|---|---|---|---|---|
| PNG | ~5 МБ | ~22 МБ | Медленно | ✅ |
| WebP | ~1 МБ | ~22 МБ | Медленно | ✅ |
| KTX2 (ETC1S) | ~0.5–0.8 МБ | ~2.8 МБ | Быстро | ✅ |
| KTX2 (UASTC) | ~3–4 МБ | ~5.6 МБ | Быстро | ✅ |
Обратите внимание: WebP занимает столько же памяти GPU, сколько PNG — он просто меньше на диске, но при загрузке в видеопамять всё равно распаковывается в исходные пиксели. KTX2 с ETC1S одновременно уменьшает и размер на диске, и в памяти GPU — в этом его главная ценность.
Инструментарий: три пути
Сжать текстуры в KTX2 можно не одним инструментом. Расположим по «удобству использования»:
1. toktx (официальный, самый мощный)
Официальный инструмент Khronos, полный набор параметров, подходит для обработки отдельных текстур.
# Преобразовать PNG в KTX2 с кодированием ETC1S
toktx --bcmp --uastc 0 albedo.ktx2 albedo.png
# Преобразовать PNG в KTX2 с кодированием UASTC
toktx --uastc 1 normal.ktx2 normal.png
Популярные параметры:
# ETC1S + уровень качества (1–255, по умолчанию 128, чем выше, тем лучше качество и больше объём)
toktx --bcmp --uastc 0 --qlevel 200 albedo.ktx2 albedo.png
# UASTC + суперсжатие (Zstandard, дополнительно уменьшает размер на диске)
toktx --uastc 1 --zcmp 19 normal.ktx2 normal.png
# Автоматическая генерация мипмапов (настоятельно рекомендуется)
toktx --bcmp --genmipmap albedo.ktx2 albedo.png
# Задать цветовое пространство sRGB (обязательно для цветных карт)
toktx --bcmp --srgb albedo.ktx2 albedo.png
--bcmp— это флаг режима ETC1S (базовый режим Basis Universal),--uastc 1— режим UASTC. Они взаимоисключающие.
2. gltf-transform (самый удобный, настоятельно рекомендуется)
Если у вас целая модель glTF/GLB, используйте gltf-transform одной командой, чтобы заменить все текстуры на KTX2, автоматически выбирая ETC1S/UASTC в зависимости от назначения.
# Установка
npm install -g @gltf-transform/cli
# Однокомандное сжатие всей модели
gltf-transform optimize model.glb model-optimized.glb \
--texture-compress basisu
Он сам определяет тип текстуры: для цветных использует ETC1S, для карт данных — UASTC, и автоматически прописывает расширение KHR_texture_basisu. Это лучший выбор для 90% сценариев — не нужно вручную вбивать toktx для каждой карты.
3. Онлайн-инструменты (самый быстрый старт)
Когда нет возможности устанавливать окружение, используйте браузерные инструменты: gltf.report (онлайн-версия gltf-transform), KTX2 Converter и т.п. Загрузил, скачал — готово. Подходит для ранних экспериментов или одноразовых задач.
Полный пайплайн: от исходника до продакшена
Стандартный процесс (схема):
Исходники (PNG/JPG/PSD/TGA)
│
├── [Целая модель] gltf-transform optimize model.glb → автоматическое определение типа текстуры
│ └─ Цветные карты → ETC1S
│ └─ Карты данных → UASTC
│ └─ Запись расширения KHR_texture_basisu
│
└── [Отдельные текстуры] toktx → ручное указание ETC1S/UASTC + цветовое пространство + мипмапы
│
▼
Сжатые KTX2 / GLB
│
▼
Загрузка в движке (Three.js / Babylon.js) ── трансформация на лету → нативный формат GPU
Загрузка KTX2 в Three.js
Three.js поддерживает KTX2 нативно с версии r129: подключите KTX2Loader (укажите путь к wasm-файлам transcoder, определите возможности GPU) к GLTFLoader, после чего загрузка glTF с текстурами KTX2 будет автоматически декодироваться.
const ktx2Loader = new KTX2Loader().setTranscoderPath("/basis/").detectSupport(renderer);
gltfLoader.setKTX2Loader(ktx2Loader); // Если модель также использует Draco/MeshOpt, не забудьте подключить DRACOLoader / MeshoptDecoder
Обязательно вызывайте detectSupport(renderer) — он определяет, в какой нативный формат делать трансформацию. Также для цветных текстур нужно установить texture.colorSpace = THREE.SRGBColorSpace. Пропуск этого шага — классическая ошибка, когда «вся картинка серая». Для карт данных (normal, roughness и т.д.) цветовое пространство остаётся линейным.
Загрузка KTX2 в Babylon.js
В Babylon.js всё проще — GLTFFileLoader по умолчанию поддерживает расширение KHR_texture_basisu и автоматически скачивает transcoder с CDN. Загрузка glTF с текстурами KTX2 работает из коробки. В офлайн-среде или внутренней сети нужно вручную настроить BASISFileLoader.TranscoderModule.
Настройка параметров сжатия: баланс качества и объёма
Ключевой параметр ETC1S — --qlevel (1–255). Как он влияет на результат:
| qlevel | Объём | Качество | Время кодирования | Применение |
|---|---|---|---|---|
| 128 (по ум.) | Маленький | Достаточно | Среднее | Большинство случаев |
| 200–255 | Больше | Близко к без потерь | Долгое (в разы) | Высокие требования к качеству |
| 60–100 | Очень маленький | Видимые блоки | Быстро | Дальний план / маленькие текстуры |
Объём UASTC относительно фиксирован, в основном регулируется параметром --zcmp (суперсжатие Zstandard), который влияет только на размер на диске, не на память GPU (после распаковки всё равно 8 бит на пиксель).
Рекомендуемый порядок настройки:
- Сначала сожмите с параметрами по умолчанию, посмотрите на объём и качество
- Если не устраивает → меняйте
--qlevel(ETC1S) или добавляйте--zcmp(UASTC) - Если карта нормалей стала размытой → убедитесь, что используется UASTC, а не ETC1S
- Если цвета стали тёмными → проверьте настройки цветового пространства (флаг sRGB и параметр colorSpace в движке)
Часто задаваемые вопросы и решение проблем
Ошибка трансформации / ошибка загрузки
- Проверьте, правильный ли путь к wasm-файлам transcoder (в Three.js нужен
setTranscoderPath) - Проверьте, поддерживает ли версия движка текущую версию KTX2 (старые кодировки Basis могут не поддерживаться новым transcoder)
- В консоли обычно есть конкретное сообщение об ошибке — ищите по ключевым словам
Цвета тёмные / светлые
- Цветная карта (albedo) не настроена на sRGB, или настроена наоборот
- При использовании toktx забыли флаг
--srgb(для цветных карт нужно) - На карту данных (normal) ошибочно добавлен sRGB
Отсутствие мипмапов, мерцание вдали
- При сжатии не добавлен
--genmipmap - В движке не включён
texture.generateMipmaps(в Three.js для KTX2 по умолчанию берётся из файла, но всё равно нужно, чтобы у материалаminFilterбыл установлен в режим мипмапов)
Приближение показывает неправильное направление нормалей
- Карта нормалей использует ETC1S — замените на UASTC
- Убедитесь, что карта нормалей в стиле OpenGL (зелёный канал направлен вверх); в некоторых движках для стиля DirectX нужно инвертировать канал G
Файл стал больше
- Для маленьких текстур (меньше 128×128) KTX2 невыгодно — блочное сжатие имеет фиксированные накладные расходы, которые занимают весь блок
- Одноцветные текстуры не стоит сжимать в KTX2 — проще задать цвет через материал
Что дальше
Сжатие текстур на этом почти освоено. Но «уметь пользоваться инструментом» не равно «уметь выбирать правильный инструмент». В следующей статье мы сведём все знания из предыдущих четырёх статей в единую систему выбора: для десктопа, мобильных устройств, VR и мини-приложений — какие комбинации использовать в каждом сценарии.