SashkoSoft

alpha-cutout-acceptance

Приёмка текстур с альфа-вырезом (растительность, грязь, декали, наклейки, листва, дым) от любого генератора — ComfyUI/Stable Diffusion, рендера, фотосканов. Два независимых числа вместо разглядывания превью — ореол подложки на кромке и качество самой кромки, — плюс объяснение, почему альфу нельзя достать из одной картинки над одним фоном. Используй, когда решаешь принимать или вернуть набор с прозрачностью; когда пятно в игре читается «вырезанным ножницами» или обведённым светлым контуром; когда исполнитель спорит с вердиктом приёмки; когда надо сформулировать требование к альфе числом, а не словами «чтобы красиво сходило на нет».

SashkoSoft 0 Updated 1mo ago

Resources

2
GitHub

Install

npx skillscat add sashkosoft/godot-skilled/alpha-cutout-acceptance

Install via the SkillsCat registry.

SKILL.md

Приёмка альфа-выреза числом

Собран на проекте, где три сессии поставляли ассеты в общий пайплайн, а
приёмка смотрела превью. Дважды ошиблись в разные стороны: один раз
вернули годный файл, один раз приняли негодный — и он полторы недели
стоял в игре (см. ORIGIN.md).

Главное правило

Превью на белом или сером фоне не показывает ни одного дефекта альфы.
Оба дефекта видны только над тёмным (0.10–0.15) — и оба меряются числом за
секунду, без единого рендера.

Дефекта ровно два, они независимы, и лечатся по-разному:

Дефект Как выглядит в игре Метрика
Ореол каждый лист обведён светлым, куст «заиндевел» scripts/halo.py
Ножевая кромка пятно обрывается ровной линией, «вырезано ножницами» scripts/alpha_edge.py

Ореол: мерить ЛОКАЛЬНО, иначе забракуешь годное

Ореол — это цвет подложки, оставшийся на полупрозрачных пикселях. Соблазн
померить его так: средняя яркость кромки (0.05 < a < 0.5) минус средняя
яркость ядра (a > 0.9) по всему файлу. Эта метрика врёт. Она смешивает
ореол с настоящей структурой: тонкая сухая метёлка и правда светлее
затенённой глубины куста, и файл с идеальной альфой получит большой плюс.

Мерить надо локально: каждый кромочный пиксель против среднего ядра в своей
окрестности 9×9. Ореол — эффект локальный, подложка светлеет вплотную к листу.

Проверка на реальном наборе, один и тот же файл:

moss_edge   глобально +0.2174   локально −0.0013

Глобально — «половина прежнего ореола, вернуть на переделку». Локально —
ореола нет вовсе. Вернули зря, исполнитель был прав и прямо об этом написал.

Порог: локально хуже +0.01 — возврат. Глобальное число в вердикт не
писать вообще, оно только путает.

Кромка: два числа, и оба обязаны сойтись

альфа_на_рамке   максимум альфы в полосе 2 px по краю холста
кайма/периметр   сколько полупрозрачных пикселей на один пиксель периметра
  • Альфа на рамке > 0 — растение упирается в край картинки. Что бы ни
    делал движок, наклейка обрежется по границе своего бокса ровной линией.
    У Decal в Godot боковой растушёвки нет вовсе; в других движках её
    тоже обычно нет. Лечится только альфой в файле.
  • Кайма/периметр < 1 — альфа почти бинарная, мягкого перехода нет.
    Нож даже в середине холста.

Калибровка по двум наборам из одного проекта:

Набор альфа на рамке кайма/периметр
рендер, Karma (годный) 0.000 26 … 550
керинг картинки (негодный) 1.000 0.11 … 0.28

Разница в два-три порядка. Промежуточных значений на практике не бывает:
либо альфа снята с рендера, либо угадана по яркости.

Порог: альфа на рамке = 0 по полосе 4 px; кайма/периметр — десятки.

Почему «покрасить на белом и вычесть фон» не работает никогда

Генератор рисует объект на подложке и отдаёт один RGB-кадр. Восстановить из
него альфу нельзя в принципе: три уравнения (R, G, B) против четырёх
неизвестных (c_r, c_g, c_b, α). «Расстояние от белого» — это догадка о
покрытии, а не измерение.

Отсюда практические следствия:

  • на тёмном плотном листе догадка хорошая, на светлой соломенной метёлке
    плохая — она почти неотличима от подложки, и покрытие систематически
    занижается
    ;
  • вычитание подложки (c = (rgb − (1 − α)) / α) при малой альфе неустойчиво
    и ореол не убирает;
  • протяжка цвета из ядра наружу (push-pull fill) убирает белизну кромки, но
    альфу не чинит — это обход, а не решение;
  • у рендера проблемы нет вовсе: он знает истинное покрытие пикселя и отдаёт
    его прямо.

Вывод для пайплайна: вырез по альфе заказывать там, где есть геометрия и
рендер. Генератору картинок оставлять то, где альфа не нужна или заведомо
плотная.

Как этим пользоваться

python scripts/halo.py       "delivery/**/*_albedo_*.png"
python scripts/alpha_edge.py "delivery/**/*_albedo_*.png"

Обе печатают строку на файл и помечают подозрительные. Числа кладутся прямо
в раздел приёмки — тогда спор с исполнителем идёт о цифре, а не о вкусе.

В задание требование пишется так же числом: «альфа = 0 по всей рамке в 4 px,
кайма/периметр в десятки, превью собрать над тёмным 0.10–0.15». И просить
свой замер от исполнителя — тогда расхождение видно на сдаче, а не через
две недели в игре.

Рабочие привычки

  • Мерить до того, как открыл превью. Превью потом — для стиля, не для альфы.
  • Вернул набор — приложи числа. Вернул без чисел — скорее всего вернул зря.
  • Исполнитель спорит с метрикой — проверь метрику, а не настаивай.
    В этом проекте спорящий был прав, а приёмка неправа.
  • Дефект альфы не чинится настройками движка. Можно замаскировать
    (подмешивание вместо полной замены альбедо), но это лечение следствия.

Categories