Sprite Fusion Sprite Normals — генератор карт нормалей для пиксель-арт спрайтов
📂 Исходный код на GitHubИнструмент на Rust для генерации карт нормалей из пиксель-арт спрайтов, листов кадров и анимаций GIF/APNG. Поставляется как CLI и как модуль WebAssembly, работает локально. Часть проекта Sprite Fusion, лицензия MIT.
Sprite Fusion Sprite Normals — инструмент, который из обычного пиксель-арт спрайта делает карту нормалей (normal map). Написан на Rust и поставляется в двух видах: как CLI для консоли и как библиотека WebAssembly для браузера. Он умеет брать одиночные спрайты, листы кадров и анимации GIF/APNG, считать из них рельеф поверхности и сохранять результат локально, на вашей машине. Онлайн-версия доступна на https://www.spritefusion.com/sprite-normals. У проекта есть и API, и подключение через MCP.
Зачем спрайту карта нормалей
Карта нормалей — это отдельная картинка, в которой для каждого пикселя записано, куда «смотрит» поверхность: вверх, вниз, влево, вправо. Движок читает эти направления и считает, как на пиксель падает свет. Обычный 2D-спрайт начинает реагировать на движущийся источник света: одна и та же картинка выглядит иначе, если свет слева или справа. Такие 2D-игры с динамическим освещением часто называют 2.5D.
У инструмента три главных свойства:
- рельеф считается из силуэта спрайта и из его собственной штриховки, рисовать карту руками не нужно;
- исходное пиксельное разрешение и прозрачность сохраняются — картинка не размывается и не увеличивается;
- спрайты, листы кадров и анимации обрабатываются локально, без отправки файлов на сервер.
Проект подходит для трёх задач. Первое — 2D-игры, в которых есть движущиеся источники света и спрайты с картами нормалей. Второе — анимации в пиксель-арте, которым нужны кадры карт нормалей, точно совпадающие с кадрами исходной анимации. Третье — конвейеры подготовки ассетов, где обработка должна идти либо через нативный CLI, либо прямо в браузере.
Для конвейера CLI удобен тем, что его легко встроить в скрипт сборки: положили спрайты в одну папку командой с --output-dir, получили рядом такую же папку с картами нормалей. Ручной проход по файлам не нужен.
CLI
Сборка из исходников
Для сборки нужен Rust версии 1.85 или новее. Из каталога проекта:
cargo build --release --locked
Флаг --locked заставляет cargo брать версии зависимостей ровно из файла Cargo.lock. Готовый бинарник окажется в target/release/spritefusion-sprite-normals. Чтобы поставить его локально:
cargo install --path . --locked
Использование
spritefusion-sprite-normals <INPUT>... [OPTIONS]
Аргумент <INPUT> — это один или несколько входных файлов: PNG-спрайты или листы кадров, а также GIF и APNG. Доступные опции:
| Опция | Что делает | Значение по умолчанию |
|---|---|---|
-o <OUTPUT> |
Куда сохранить карту нормалей для одного входа | — |
--output-dir <DIR> |
Каталог для пакетной обработки | — |
--normal-strength <VALUE> |
Общая сила рельефа | 1.5 |
--detail-strength <VALUE> |
Рельеф из нарисованной штриховки | 6 |
--detail-scale <PIXELS> |
Сглаживание штриховки | 0.6 |
--shape-strength <VALUE> |
Объём из силуэта | 1.15 |
--frame-size <WIDTHxHEIGHT> |
Разрезать равномерный PNG-лист на ячейки | — |
--normal-format directx |
Перевернуть зелёный канал; по умолчанию OpenGL | OpenGL |
--force |
Перезаписывать существующие результаты; входные файлы защищены | выкл. |
Опции делятся на две группы. --shape-strength отвечает за общий объём, который инструмент выводит из силуэта. --detail-strength и --detail-scale отвечают за мелкий рельеф, который он выводит из нарисованной штриховки. --normal-strength задаёт общую силу карты: логично подбирать его последним, когда две другие группы уже настроены.
Примеры:
spritefusion-sprite-normals sprite.png -o sprite_n.png
spritefusion-sprite-normals sprites/*.png --output-dir normals
spritefusion-sprite-normals sprite.png -o sprite_n.png --normal-strength 2
spritefusion-sprite-normals sheet.png --frame-size 32x32 -o sheet_n.png
spritefusion-sprite-normals walk.gif -o walk_n.png
Если не указывать путь вывода, файлы сохраняются рядом с исходниками с именем вида <name>_n.png. Полный список опций даёт команда spritefusion-sprite-normals --help. Опция --force перезаписывает уже готовые результаты, но входные файлы защищены: инструмент никогда не пишет поверх оригиналов.
Формат по умолчанию — OpenGL. Если ваш движок ждёт карту нормалей в формате DirectX, зелёный канал надо перевернуть — это делает опция --normal-format directx.
Анимации
GIF, APNG и PNG-листы с опцией --frame-size дают на выходе три файла. На примере анимации walk:
walk_n.png— атлас карт нормалей без потерь;walk_n.color.png— цветной атлас с той же раскладкой кадров;walk_n.frames.json— размеры атласа, размеры кадра, число колонок, число кадров и тайминг.
Кадры уложены построчно, слева направо и сверху вниз. В поле delays_ms лежат пары [числитель, знаменатель] в миллисекундах. PNG-листы сохраняют свою раскладку и получают задержку кадра по умолчанию 100 мс. Сколько раз анимация зациклена в исходнике, в результат не выгружается.
Есть важная деталь по работе с такими файлами в движке. PNG с картой нормалей — это не картинка для показа, а файл с данными: отключите для него sRGB-сэмплирование в движке. Рисуйте арт в его родном пиксельном разрешении. Сама генерация оценивает рельеф по цвету и форме, поэтому нарисованные художником блики тоже могут превратиться в детали поверхности.
WebAssembly
Сам модуль для браузера собирается под цель wasm32-unknown-unknown и требует CLI wasm-bindgen версии 0.2.126:
cargo build --release --target wasm32-unknown-unknown --no-default-features --locked
wasm-bindgen target/wasm32-unknown-unknown/release/spritefusion_sprite_normals.wasm --target web --out-dir pkg
Готовый пакет подключается в проект так:
import init, { generate_normal_map } from "./pkg/spritefusion_sprite_normals.js";
await init();
const result = generate_normal_map(inputBytes, JSON.stringify({ strength: 1.5 }));
try {
const normalPng = result.normal_png;
const colorPng = result.color_png;
const metadata = JSON.parse(result.metadata);
// Use these bytes to save files or create image URLs.
} finally {
result.free();
}
Первый аргумент — Uint8Array с байтами входного файла, второй — строка с настройками; передайте "{}", если нужны значения по умолчанию. Доступные ключи: strength, detail, smoothing, volume, frame_width, frame_height, directx. Байты в возвращаемых массивах принадлежат этим массивам — после вызова free() они остаются валидными.
CLI и WASM-модуль используют один и тот же генератор и один декодер анимаций, так что результат в браузере и в консоли совпадает.
Ограничения на вход
CLI и WASM-модуль работают на одном коде, поэтому ограничения на вход общие:
- размер входного файла — до 64 МиБ;
- до 4 194 304 пикселей вместе с отступами для обработки;
- для анимаций дополнительно — до 256 кадров и 4 194 304 декодированных пикселей суммарно.
Разработка
Стандартная проверка для Rust-проекта:
cargo test --locked
cargo clippy --all-targets --locked -- -D warnings
cargo fmt --check
Связанные проекты и лицензия
Sprite Normals — часть проекта Sprite Fusion. Сам Sprite Fusion — инструмент для генерации пиксель-арт спрайтов и анимаций в разработке игр.
Из других открытых инструментов для пиксель-арта в README отмечен Pixel Snapper — он приводит пиксели к чистой сетке и сделан специально, чтобы приводить в порядок пиксель-арт, сгенерированный ИИ.
Лицензия — MIT, автор — Hugo Duprez.