Магія кастомних шейдерів: як я перестав їх боятися
Вітаю, спільното!
Одразу зазначу, що це не тутоіал, а міні девлог.
Це авторський текст. Він народився не з натхнення ззовні, а з того стану захоплення, який з’являється під час роботи. Бо розробка ігор — це постійні відкриття, майже як у справжній науці.
Не впевнений, що мене вистачить на цілу серію, але вирішив почати з першої статті про шейдери. Раніше я свідомо оминав тему власних шейдерів: по-перше, здавалось, що це щось надто складне; по-друге, я був переконаний, що стандартні рішення покривають 99% графічних задач. Лише зараз стало зрозуміло, скільки можливостей я втрачав. Колись я навіть купував готові шейдери — наприклад, для ефекту привида за $10. Сьогодні це виглядає майже комічно, бо подібне робиться за кілька хвилин.
Скріншот з прикладом роботи купленого шейдера
Світ шейдерів відкрився для мене відносно нещодавно — і швидко став улюбленою частиною розробки. Якщо пояснювати просто, Unity Shader Graph — це щось середнє між спрощеним і ускладненим програмуванням. Деякі речі справді легше зробити кодом, але можливості, які дає Shader Graph, важко переоцінити.
Щоб не говорити абстрактно, покажу маленький приклад із поточної роботи. Зараз я роблю idle-гру, і для ключової механіки мені знадобилось коло з переривчастим контуром. Два місяці тому я вирішив це без шейдерів — просто намалював кілька варіантів спрайта під різні масштаби.

У грі відбувався банальний sprite swap: підставлявся той варіант, який був найближчим до потрібного масштабу. Просте рішення, але з типовими проблемами — розмиття, розтягування, обмеження. Учора я вирішив, що це вже виглядає несерйозно, і зробив власний шейдер, який малює таке коло в будь-якому масштабі — pixel perfect.
У студентські роки геометрія давалась мені легко, а от тригонометрія — значно гірше. Але за роки в геймдеві вона стала інтуїтивно зрозумілою. Далі — коротко про сам принцип.
Починаємо (як в 99% випадках) з UV. Це двовимірна розгортка зі значеннями від 0 до 1 — координатна система, яка каже шейдеру, де саме знаходиться піксель на текстурі. Розиваємо UV на 2 осі (X та Y) та отримуємо базові вертикальний та горизонтальний градієнти.

Наше завдання — рівномірно розбити коло на сегменти по периметру. Логічний перший крок — тригонометрія: синуси, косинуси, радіани. Беремо X і Y з UV, переносимо центр координат у середину, нормалізуємо значення.
Центром координат для нашого кола має бути центр UV розгортки тому нормалізуємо наші значення з формату від 0 до 1 до формату [1 0 1]. насправді нам підійде і [-1 0 1] але знак мінус в нашому випадку на результат не впливає а от shader graph всі занчення з межами

Ми підготували вхідні дані й можемо переходити до базової тригонометрії. Маючи координати X та Y кожного пікселя відносно центру спрайта, можна визначити його кут відносно цього центру в радіанах. Для цього використовуємо atan2 (досі до кінця не розумію, як воно працює, але воно повертає кут у радіанах, який за потреби можна перевести в градуси).
На виході отримуємо щось дуже схоже на наші попередні X та Y відносно центру — але тепер це вже не лінійна залежність, а радіальна (математики, виправте мене).

Далі, маючи ці значення в радіанах, просто застосовуємо косинус — і на цьому етапі отримуємо щось, що виглядає як старий прапор Японії.

80% роботи вже зроблено. Залишилось лише зібрати все докупи — нормалізуємо цей «градієнтний прапор» до двох кольорів.

Проганяємо це через Saturate, тому що, попри зовнішній вигляд, значення насправді лежать не в межах
Саме коло також можна було б побудувати через синуси та косинуси. Я навіть почав це робити в перші дві хвилини — поки не згадав, що в Unity Shader Graph є готова нода для малювання кола. Тож просто беремо з неї периметр.

Далі залишається по суті скласти коло з нашим «прапором Японії» — тобто перемножити їх так, щоб білими залишилися лише зони перетину цих двох «текстур».

Усі ці маніпуляції були потрібні лише для отримання маски для альфа-каналу. Очевидно, я опустив кілька технічних деталей, де додається:
- анімація обертання
- різні дрібні, але важливі нормалізації
- фарбування
але в межах цього прикладу це не критично.

Комусь це може здатися занадто складним для простої задачі — і що простіше було залишитися на варіанті з кількома спрайтами з фотошопу та їх свапом. Можливо. Але зараз я швидше зроблю шейдер, ніж витрачатиму час на механічну роботу й зайві асети.
Я б радив не оминати тему власних кастомних шейдерів. Це значно простіше, ніж здається, і відкриває доступ до справжньої магії. Особисто мені це дало друге дихання в інді-геймдеві.
У цьому ж проєкті я працював і з більш складними речами — наприклад, із системами вогню, рідин та газів з використанням compute shader-ів. Можливо, колись опишу й цей досвід.
Дякую за увагу. Радію, що знайшов час це написати. Сподіваюся, комусь було цікаво.
2 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів