Руководство для начинающих
Что происходит с текстурой внутри GPU
PNG размером 300 КБ может незаметно занять 5 МБ видеопамяти. Это руководство объясняет почему, как GPU уменьшают текстуры с помощью блочного сжатия и как выбрать формат. К каждой идее прилагается то, что можно попробовать самому.
Глава 1
Размер файла — не размер в памяти
PNG и JPEG созданы для хранения и передачи изображений. Прежде чем GPU сможет нарисовать текстуру, изображение распаковывается в «сырые» тексели — обычно по 4 байта на каждый.
GPU выбирает эти тексели миллионы раз за кадр, поэтому они должны быть всегда готовы к чтению. Значит, расход памяти зависит только от разрешения и формата пикселей, а не от того, насколько хорошо был сжат файл. Мип-уровни — уменьшенные копии для удалённых поверхностей — добавляют ещё примерно треть.
- RGBA832 бит на тексель213 MiBЭталон21,3 MiB на текстуру
- BC7 · ASTC 4×48 бит на тексель53,3 MiBв 4× меньше5,3 MiB на текстуру
- BC1 · ETC2 RGB4 бит на тексель26,7 MiBв 8× меньше2,7 MiB на текстуру
- ASTC 6×63,56 бит на тексель23,8 MiBв 9× меньше2,4 MiB на текстуру
- ASTC 8×82 бит на тексель13,3 MiBв 16× меньше1,3 MiB на текстуру
- ASTC 12×120,89 бит на тексель6 MiBв 35,7× меньше612 KiB на текстуру
Блочные форматы округляются вверх до целых блоков на каждом мип-уровне, поэтому числа совпадают с тем, что реально выделяет GPU.
Глава 2
Почему GPU не может хранить PNG
Когда шейдер выбирает данные из текстуры, он может запросить любой тексель в любом порядке. Оборудование должно найти его мгновенно.
PNG и JPEG используют кодирование переменной длины: чтобы добраться до пикселя в середине, нужно сначала декодировать всё, что идёт перед ним. Форматы GPU имеют фиксированную скорость: каждый блок 4×4 занимает ровно одинаковое число байтов, поэтому адрес любого текселя вычисляется простым умножением. Текстура остаётся сжатой в памяти, а текстурный блок декодирует данные при чтении.
Перемещайте выделение клавишами со стрелками.
- Выбранный тексель
- x 13, y 18
- Его блок
- столбец 3, строка 4
- Адрес в BC1
- (4 × 8 + 3) × 8 байт = байт 280
64 блока в том порядке, в каком они лежат в памяти
Тот же тексель в PNG
Сначала нужно распаковать строки с 0 по 18 (19 из 32)
Глава 3
Внутри блока 4×4
BC1, самый старый из широко используемых форматов, нагляднее всего показывает идею. Он хранит блок из 16 текселей всего в 8 байтах.
Кодировщик выбирает два опорных цвета. Декодер смешивает их в четыре цвета на прямой линии, а каждый тексель хранит 2-битный номер ближайшего из них. Блоки с плавными переходами цвета сохраняются хорошо, а блоки с несколькими несвязанными цветами — нет.
Глава 4
Много форматов — разные компромиссы
Новые форматы расходуют биты разумнее, чем BC1: больше опорных точек, несколько цветовых линий в блоке, лучший альфа-канал или выбор размера блока.
BC7 и ASTC 4×4 используют 8 бит на тексель и выглядят очень близко к оригиналу. ASTC меняет качество на размер через размер блока: блок всегда занимает 128 бит, поэтому блок 12×12 стоит меньше одного бита на тексель. Basis Universal идёт другим путём: он поставляет один компактный файл и перекодирует его на устройстве в формат, который поддерживает этот GPU.
| Формат | Бит на тексель | Альфа | Типичные платформы | Лучше всего для |
|---|---|---|---|---|
| BC1 | 4 | 1 бит | ПК, консоли | Непрозрачный цвет при нехватке памяти |
| BC3 | 8 | Да | ПК, консоли | Цвет с плавной альфой в старом контенте |
| BC4 | 4 | Каналы данных | ПК, консоли | Один канал: маски, шероховатость, высота |
| BC5 | 8 | Каналы данных | ПК, консоли | Карты нормалей (X и Y) |
| BC6H | 8 | Нет | ПК, консоли | HDR-цвет, например скайбоксы и карты освещения |
| BC7 | 8 | Да | ПК, консоли | Качественный цвет с альфой или без неё |
| ETC1 | 4 | Нет | Почти все Android | Непрозрачный цвет на очень старых устройствах |
| ETC2 | 4–8 | Да | Android, iOS | Широко поддерживаемый запасной вариант для мобильных |
| ASTC | 0.89–8 | Да | Современные Android и iOS, Apple Silicon | Настраиваемое качество на мобильных |
| PVRTC | 2–4 | Да | Старые iPhone и iPad | Только старые устройства Apple |
| Basis ETC1S | Зависит от целевого формата | Да | Любой GPU через KTX2 | Маленькие загрузки, работает на любом GPU |
| Basis UASTC | Зависит от целевого формата | Да | Любой GPU через KTX2 | Высокое качество, работает на любом GPU |
Нажмите «Запустить кодировщики», чтобы сжать пример. При первом запуске кодировщики будут загружены.
Глава 5
Как выбрать формат
Два вопроса быстро сужают выбор: какие GPU должны загружать текстуру и какие данные она содержит?
Цветовые текстуры, карты нормалей и одноканальные маски по-разному реагируют на сжатие, а настольные, мобильные и веб-платформы поддерживают разные семейства форматов. Выберите свой случай, чтобы получить разумную отправную точку, а затем проверьте результат на своих текстурах.
Начните с
ASTC 6×6
Также подойдёт: ASTC 4×4 · ETC2 RGB
ASTC работает на современных GPU Android и iOS. 6×6 — хороший баланс; для ключевых ресурсов используйте 4×4, а для старых устройств — ETC2.
Открыть кодировщик ASTCГлава 6
Практические советы
Несколько привычек предотвращают большинство проблем со сжатием текстур.
- 01
Помечайте цвет как sRGB, а данные — как линейные
Текстуры альбедо и интерфейса хранятся в sRGB. Карты нормалей, шероховатости и другие данные должны оставаться линейными, иначе освещение будет неверным.
- 02
Отдельный формат для карт нормалей
Используйте двухканальные форматы, такие как BC5 или EAC RG11, и восстанавливайте Z в шейдере. Цветовые форматы тратят биты впустую и добавляют заметные неровности.
- 03
Генерируйте мип-уровни
Они добавляют около 33% памяти, но убирают мерцание удалённых поверхностей и ускоряют выборку.
- 04
Подбирайте размеры под блоки
Большинство форматов работает блоками 4×4, поэтому размеры, кратные 4, не требуют дополнения. Старым форматам вроде PVRTC также нужны квадратные размеры, равные степени двойки.
- 05
Проверяйте реальный контент на реальных устройствах
Сравните декодированный результат с оригиналом при полном увеличении и убедитесь, что целевые GPU поддерживают формат.
Частые вопросы
Сжатие текстур GPU происходит без потерь?
Нет. BC, ETC, ASTC и PVRTC — форматы с потерями и фиксированным размером блока. Насколько упадёт качество, зависит от формата, размера блока и изображения.
Почему бы не использовать PNG или JPEG как текстуры напрямую?
Они быстрее скачиваются, но GPU не может выбирать из них данные, поэтому они декодируются в «сырые» тексели и занимают полный объём памяти. Форматы GPU остаются компактными и в памяти.
Какой формат текстур лучше всего?
BC7 — надёжный выбор по умолчанию на ПК, ASTC — на современных мобильных устройствах, а KTX2 с Basis Universal — когда один файл должен работать на многих GPU, например в вебе.
Что значит «бит на тексель»?
Это средние затраты памяти на один тексель. RGBA8 без сжатия использует 32 бита, BC1 — 4, BC7 — 8, а ASTC — от 8 до примерно 0,89 в зависимости от размера блока.
Попробуйте на своих текстурах
Все кодировщики работают локально в браузере. Загрузите PNG или JPEG, выберите формат и сравните результат.