Введення в програмування шейдеров для початківців
Навчившись писати шейдери, ви зможете максимально ефективно використовувати всю обчислювальну потужність сучасних графічних чіпів, тисячі ядер яких працюють паралельно в одному потоці, адже все шейдерниє обчислення проводяться на GPU, а не на CPU. Програмування шейдерів вимагає іншого мислення і підходу до написання коду, ніж написання звичайних програм, однак їх практично безмежний потенціал з лишком окупає всі проблеми на початкових етапах.
На сьогоднішній день практично будь-які візуальні ефекти в іграх і подібних програмах працюють на GPU-коді, починаючи з реалістичного освітлення в ААА-іграх і закінчуючи ефектами постобработки 2D графіки та симуляцією рідини.

Цілі даного уроку
Багато людей сприймають програмування шейдеров як щось незрозуміле з галузі чорної магії. У мережі можна знайти величезну кількість зразків коду для створення чудових ефектів, однак практично ніяких пояснень до них немає. Даний урок якраз спрямований на те, щоб подолати ці перешкоди і розвіяти всі помилки щодо шейдеров. Основний фокус даного уроку - досягти розуміння того, як писати і розуміти код шейдеров, починаючи з самих азів, щоб Новомосковсктель міг з легкістю писати шейдери з нуля, комбінувати і модифікувати існуючі.
Це урок загального призначення, тому ви зможете застосувати свої знання на будь-якій платформі, що підтримує шейдери.
Отже, що ж таке взагалі шейдер?
Шейдер - це програма, яка виконується в циклі рендеринга графіки і повідомляє комп'ютера, як обробляти і виводити на екран кожен піксель. Ці програми називають шейдерами, тому що дуже часто їх використовують для контролю ефектів освітлення і створення реалістичних тіней (від англійського shade - тінь), проте немає ніяких причин і обмежень не використовувати їх для створення інших спецефектів.
Шейдери пишуться на особливому мовою програмування. Однак не варто хвилюватися. Вам не доведеться вчити з нуля абсолютно нову мову, оскільки для написання шейдеров ми будемо використовувати GLSL, синтаксис якого дуже схожий на інші C-подібні мови. (Насправді, існує досить велика кількість мов для написання шейдеров. Однак, з огляду на те, що всі вони призначені для виконання на GPU, вони все практично однакові)
Отже, приступимо!
Для цього уроку ми будемо використовувати ShaderToy. Цей ресурс дозволяє писати шейдери прямо в вашому браузері, не обтяжуючи себе ніякими додатковими настройками. (Однак для використання ShaderToy вам знадобиться браузер з підтримкою WebGL, оскільки саме він використовується для рендеринга.) Реєстрація не обов'язкова, однак вона дозволить зберегти свій код на майбутнє в профілі.
Прим .: На момент написання статті ресурс знаходиться в стадії бета-тестування. Тому можливо, що на момент прочитання статті, елементи інтерфейсу / синтаксису будуть незначно відрізнятися.
Натиснувши на New Shader. ви повинні побачити ось таке вікно:
Отже, що ж тут відбувається?
Зараз я поясню одним реченням, як працюють шейдери. Чи готові? Поїхали!
Основне завдання шейдера - надати на виході чотири числа, які виражаються букваміr. g. b. a.
Давайте приймемо все вищевикладене до уваги і спробуємо заповнити екран червоним кольором. Значення rgba (red - червоний, green - зелений, blue - синій і alpha - альфа-канал, або прозорість, кажучи простіше) можуть змінюватися від 0 до 1. тому все, що нам необхідно зробити, це повернути значення r, g, b , a = 1, 0, 0, 1. у ShaderToy ці дані записуються в змінну fragColor.
Вітаю! Ось ви і написали свій перший працюючий шейдер!
Завдання. Чи зможете ви змінити колір на сірий?
vec4 - це тип даних (чотиривимірний вектор), тому ми могли б записати колір в змінну типу vec4 і передати її значення у fragColor:
Звичайно, заповнювати весь екран одним кольором не надто захоплююче. Ми можемо запустити наш код на сотнях тисяч пікселів одночасно, а ми всього лише зафарбовує їх все в один колір.
Давайте спробуємо заповнити екран градієнтом. У нас не вийде це зробити, якщо ми не знаємо координат пікселя, на колір якого ми хочемо впливати.
Вхідні дані шейдера.
Піксельний шейдер надає вам кілька вбудованих змінних. які можна використовувати в роботі. Зараз для нас буде корисною змінна fragCoord. яка містить інформацію про поточні координати пікселя на екрані. Давайте спробуємо зафарбувати всі пікселі в лівій половині екрану чорним, а в правій - червоним:
Прим. До компонентів будь-якої змінної типу vec4 можна звертатися за допомогою obj.x. obj.y. obj.z і obj.w. або obj.r. obj.g. obj.b. obj.a. Ці набори компонентів нічим не відрізняються - це просто різні способи представлення одних і тих же даних для поліпшення Новомосковскбельності коду (наприклад, якщо ви використовуєте змінну vec4 obj для позначення кольорів, інші люди, побачивши в коді позначення obj.r. відразу це зрозуміють).
Чи бачите ви проблему в коді, який ми тільки що написали? Спробуйте перейти на повноекранний режим. натиснувши на відповідну кнопку в правому нижньому кутку.
Частина екрану, пофарбована в червоний колір, буде відрізнятися в залежності від розмірів екрану. Для того, щоб бути впевненими в тому, що рівно половина екрану буде зафарбована в червоний колір, нам необхідно знати його розміри. Розмір екрану не записаний у вбудованій змінної, як, наприклад, позиція поточного пікселя, тому що зазвичай програміст сам задає розміри робочої області для свого шейдера. У нашому випадку розробники платформи ShaderToy задають ці розміри.
Якщо нам потрібно скористатися якоюсь інформацією ззовні, яка не є вбудованою змінною, ми можемо передати її з CPU (ваша основна програма) в GPU (ваш шейдер). ShaderToy вже подбав про все за нас. Значення всіх змінних, які передаються в шейдер ззовні, можна подивитися у вкладці Shader Inputs. Змінні, які передаються з CPU в GPU таким чином, в мові GLSL позначаються словом uniform.

Давайте покращимо наш код так, щоб він коректно розпізнавав, де знаходиться центр екрану. Нам буде потрібно використовувати змінну iResolution з вкладки Shader Inputs:
Якщо перейти в повноекранний режим в цей раз, то тепер екран буде розділений квітами рівно навпіл.
Переходимо до градієнту.
Перетворити поділ кольором чітко навпіл на плавний градієнт не дуже складно. Кольорові значення змінюються в межах від 0 до 1. також як і координати екрану змінюються від 0 до 1. Скористаємося цим.
Завдання. чи зможете ви зробити вертикальний градієнт замість горизонтального? А як щодо діагонального? А як щодо градієнта з декількох квітів?
Якщо ви приділите модифікаціям і тестування цього коду достатньо часу, то повинні здогадатися, що верхній лівий кут екрану має координати (0,1) замість очікуваних (0,0). Дуже важливо пам'ятати про це при написанні шейдеров.
малюємо картинки
Грати з квітами, звичайно, весело, але якщо ми захочемо створити щось дійсно вражаюче, то наш шейдер повинен вміти обробляти інформацію про зображення. Таким чином можна буде створити шейдер, який впливає або на весь екран цілком (наприклад, корекція кольору або ефект підводного світу), або лише на окремі об'єкти в залежності від інформації, що надходить (наприклад, система освітлення).

Натисніть на iChannel0 і виберіть будь-яку текстуру або зображення, яке вам подобається.
Тепер інформація про це зображенні передається вашому шейдеру. Однак, є одна проблема - у шейдера немає вбудованої функції DrawImage (). Згадаймо, що єдине, що може робити шейдер - це міняти колір кожного пікселя окремо.
Отже, якщо ми можемо повертати значення кольору кожного пікселя, як же нам намалювати нашу текстуру? Нам потрібно якимось чином співвіднести координати поточного оброблюваного пікселя з відповідним пікселем текстури:

Це можна зробити, використовуючи вбудовану функцію texture2D (texture, coordinates). яка приймає в якості параметрів текстуру і пару координат (x, y). і повертає колір текстури в цьому місці у змінній типу vec4.
Співвідносити координати текстури з екранними координатами можна будь-яким зручним способом. Можна намалювати всю текстуру лише на чверті екрана (пропускаючи пікселі - тобто зменшити її масштаб) або намалювати лише частина текстури.
Зараз нам важливо просто побачити зображення на екрані, тому будемо співвідносити всі пікселі один до одного:
Отже, ось і наш перший шейдер із зображенням!

Тепер, коли ви вмієте коректно витягати інформацію з текстури, ви можете змінювати її як вам завгодно! Її можна розтягувати, масштабувати, або змінювати колірні значення.
Давайте спробуємо додати градієнт, як робили вище:

Вітаю, ви тільки що створили свій перший постпроцессінговий ефект!
Завдання. чи зможете ви написати шейдер, який робить будь-яке зображення чорно-білим?
Оживимо наш шейдер
До цього всі ефекти, які ми створювали, були статичними. Насправді, використовуючи ті дані, які дає ShaderToy, можна створювати набагато цікавіші речі. iGlobalTime - це змінна, яка постійно збільшується з часом. Її можна використовувати в якості параметра для створення періодичних ефектів. Давайте спробуємо пограти з квітами:
У GLSL є вбудовані функції синуса і косинуса, так само як і безліч інших корисних функцій, таких як довжина вектора або відстань між векторами. Колір не може приймати від'ємне значення, тому потрібно використовувати вбудовану функцію abs, яка повертає абсолютне значення (модуль) числа без урахування знака.
Завдання. Чи зможете ви написати шейдер, який змінює зображення з чорно-білого в повнокольорове і назад?
Невелика замітка з налагодження шейдерів
Напевно ви звикли контролювати ваш код практично по рядках та виводити в консоль всі необхідні значення і використовувати breakpoints, щоб побачити, що відбувається при виконанні програми. На жаль, з шейдерами так зробити не вийде. Напевно ви зможете знайти якісь інструменти для налагодження шейдерів під обрану платформу, але в основному кращим рішенням буде встановлювати значення тих змінних, які ви захочете перевірити, такими, щоб результат можна було побачити.
висновок
Все перераховане вище - лише основи роботи з шейдерами, проте саме розуміння цих основ дозволить вам досягти набагато більшого. Подивіться ефекти, які створюють на ShaderToy інші користувачі і спробуйте розібратися або самостійно відтворити деякі з них.
Є одна річ, яка не освітлена в цій статті - це вертексниє шейдери. Їх пишуть на тій же самій мові, єдина різниця полягає в тому, що вони виконуються не попиксельно, а (як зрозуміло з назви) на кожній вершині, і повертають не тільки колір, а й позицію вершини. Вертексниє шейдери зазвичай відповідальні за проектування і відтворення зображення 3D-сцени на екрані (возмжоность, вбудована в більшість графічних движків). Піксельні ж шейдери відповідальні за створення просунутих візуальних ефектів, саме тому ми звернули основну увагу саме на них.