KTX2 на практике: правильный путь сжатия текстур
Предыдущая статья разобрала форматы текстур GPU, Basis Universal и KTX2. Теория понятна; эта статья — полностью практическая: как выбирать между ETC1S и UASTC, какие инструменты использовать, какие команды вводить и как загружать их в движке.
Вы можете следовать инструкциям и выполнять шаги по мере чтения.
Первое и самое важное: ETC1S или UASTC
Basis предлагает два режима промежуточного кодирования. Выберите неправильно — и проблема будет не в «недостаточном качестве», а в том, что ваша карта нормалей превратится в кашу. Запомните эту таблицу:
| ETC1S | UASTC | |
|---|---|---|
| Коэффициент сжатия | Очень высокий (аналог JPG) | Средний (качественный, аналог PNG) |
| Качество | Подходит для цветовых карт | Почти оригинальное качество |
| VRAM (после транскодирования) | Обычно 4bpp (~1/8 оригинала) | Обычно 8bpp (~1/4 оригинала) |
| Скорость кодирования | Медленная (настраиваемый уровень) | Быстрее |
| Использовать для | альбедо/диффузия, эмиссия | нормали, металличность-шероховатость, данные |
| Не использовать для | нормали, изображения, требующие точных значений | Цветовые карты (избыточно, размер растёт) |
Почему карты нормалей нельзя использовать с ETC1S? Потому что карта нормалей хранит векторы направлений, и RGB-каналы каждого пикселя взаимно ограничивают друг друга (длина вектора ≈ 1). ETC1S — блочное сжатие, разработанное для того, чтобы «цвета выглядели правильно»; оно не чувствительно к точности отдельных каналов, поэтому после сжатия направление вектора «уходит», и освещение сразу выглядит неправильно — особенно позиции бликов и высокочастотные детали. UASTC лучше сохраняет числовые значения и выдерживает требования к точности.
Практические правила:
- Цветовые карты (альбедо, эмиссия) → ETC1S
- Карты данных (нормали, шероховатость, металличность, AO, толщина) → UASTC
- Не уверены, но хотите сэкономить размер → попробуйте сначала ETC1S; если при приближении становится размытым, переключитесь на UASTC
Одно изображение, четыре формата для сравнения
В качестве эталона используется карта альбедо 2048x2048 (значения взяты из типичных community-данных, только для справки):
| Формат | Размер на диске | Использование VRAM (с мипмапами) | Загрузка в GPU | Кроссплатформенность |
|---|---|---|---|---|
| PNG | ~5 МБ | ~22 МБ | Медленная | ✅ |
| WebP | ~1 МБ | ~22 МБ | Медленная | ✅ |
| KTX2 (ETC1S) | ~0.5-0.8 МБ | ~2.8 МБ | Быстрая | ✅ |
| KTX2 (UASTC) | ~3-4 МБ | ~5.6 МБ | Быстрая | ✅ |
Обратите внимание: использование VRAM у WebP такое же, как у PNG — он только маленький на диске; в VRAM он всё равно распаковывается в сырые пиксели. KTX2 с ETC1S одновременно снижает и размер на диске, и VRAM — именно в этом его ценность.
Инструменты: три дороги ведут к одной цели
Для сжатия 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 — указывающий на basis-transcoder wasm и проверяющий поддержку GPU — к GLTFLoader; любой glTF с текстурами KTX2 затем автоматически транскодируется при загрузке.
const ktx2Loader = new KTX2Loader().setTranscoderPath("/basis/").detectSupport(renderer);
gltfLoader.setKTX2Loader(ktx2Loader); // также подключите DRACOLoader / MeshoptDecoder, если модель их использует
detectSupport(renderer) обязателен — он определяет, в какой нативный формат транскодировать. А для цветовых карт установите texture.colorSpace = THREE.SRGBColorSpace; пропуск этого шага — классическая ошибка «всё выглядит серым» (карты данных, такие как нормали/шероховатость, остаются в линейном пространстве).
Загрузка KTX2 в Babylon.js
В Babylon ещё проще — GLTFFileLoader по умолчанию включает KHR_texture_basisu и автоматически загружает basis-transcoder с CDN, поэтому glTF с текстурами KTX2 просто работает. Для офлайн/внутрисетевых сред вручную настройте BASISFileLoader.TranscoderModule.
Настройка параметров сжатия: баланс между качеством и размером
Основной параметр ETC1S — --qlevel (1-255). Как он влияет на результат:
| qlevel | Размер | Качество | Время кодирования | Применение |
|---|---|---|---|---|
| 128 (по умолчанию) | Маленький | Достаточное | Среднее | Большинство случаев |
| 200-255 | Бо́льший | Почти без потерь | Длинное (в несколько раз) | Высокие требования к качеству |
| 60-100 | Очень маленький | Видимые блочные артефакты | Быстрое | Далёкие/маленькие текстуры |
Размер UASTC относительно фиксирован; настройка размера на диске в основном осуществляется через --zcmp (суперсжатие Zstandard), которое не влияет на VRAM (после распаковки всё равно 8bpp).
Рекомендуемый порядок настройки:
- Сначала сожмите один раз с настройками по умолчанию и проверьте размер и качество
- Не устраивает → настройте
--qlevel(ETC1S) или добавьте--zcmp(UASTC) - Карта нормалей стала размытой → убедитесь, что используется UASTC, а не ETC1S
- Цвета кажутся тёмными → проверьте настройки цветового пространства (флаг sRGB, colorSpace в движке)
Типичные проблемы и их решение
Ошибка транскодирования / ошибка загрузки
- Проверьте, что путь к wasm транскодера корректен (Three.js требует
setTranscoderPath) - Проверьте, поддерживает ли ваша версия движка текущую версию KTX2 (старые кодировки Basis не поддерживаются новыми транскодерами)
- В консоли обычно есть конкретная ошибка; ищите по ключевым словам
Цвета слишком тёмные / слишком светлые
- Цветовая карта (альбедо) не была установлена в sRGB или установлена наоборот
- Вы забыли указать
--srgbв toktx (цветовые карты требуют его) - Карта данных (нормали) по ошибке получила sRGB
Отсутствуют мипмапы, мерцание на расстоянии
- При сжатии не добавлен
--genmipmap - Движок не включил
texture.generateMipmaps(в Three.js KTX2 следует файлу по умолчанию, ноminFilterматериала всё равно требует режим мипмап)
Неправильное направление нормалей при приближении
- Карта нормалей использовала ETC1S; переключитесь на UASTC
- Убедитесь, что карта нормалей в стиле OpenGL (зелёный канал вверх); стиль DirectX требует инвертирования канала G в некоторых движках
Файл стал больше вместо уменьшения
- Маленькие текстуры (< 128x128) не стоят KTX2 — блочное сжатие имеет фиксированный overhead и заполняет целый блок
- Не используйте KTX2 для текстуры сплошного цвета; значение цвета материала будет дешевле
Что дальше
Это в целом закрывает тему сжатия текстур. Но «знать, как пользоваться инструментами» — это не одно и то же, что «использовать правильные инструменты». Следующая статья свяжет все знания из предыдущих четырёх в единую рамку выбора: десктоп, мобильные, VR, Mini Programs — какая комбинация подходит каждому сценарию.