add ukrainian translation

pull/402/head
Manoylov Andriy 11 months ago committed by AndyMan
parent bff9800a14
commit eb8d3c5f80

@ -0,0 +1,47 @@
# Вступ
<canvas id="custom" class="canvas" data-fragment-url="cmyk-halftone.frag" data-textures="vangogh.jpg" width="700px" height="320px"></canvas>
Наведені вище зображення були зроблені різними способами. Перше намальовано рукою Ван Гога з поступовим нанесенням фарби шар за шаром. Це зайняло в нього години. Друге було створено миттєво за допомоги комбінації чотирьох матриць пікселів різних кольорів: ціанового, пурпурового, жовтого та чорного. Ключова відмінність полягає в тому, що друге зображення отримано не послідовним чином, тобто не поступово крок за кроком, а усе одночасно.
Ця книга про революційну техніку обчислювання - *фрагментні шейдери* - яка виводить генерацію цифрових зображень на новий рівень. Ви можете думати про це як про своєрідний еквівалент преса Гутенберга для графіки.
![Gutenberg's press](gutenpress.jpg)
Фрагментні шейдери дають вам повний контроль над пікселями екрану із надзвичайно високою швидкістю. Ось чому вони використовуються у самих різних випадках, від відеофільтрів на мобільних телефонах до неймовірних 3D-ігор.
![Journey by That Game Company](journey.jpg)
У наступних розділах ви дізнаєтесь, наскільки неймовірно швидка і потужна ця техніка та як застосувати її у своїх професійних та особистих проєктах.
## Для кого ця книга?
Книга написана для творчих програмістів, розробників ігор та інженерів, які мають досвід програмування, базові знання лінійної алгебри та тригонометрії й бажають вивести свою роботу на захопливий новий рівень графічної якості. Якщо ви тільки починаєте і хочете навчитися програмувати, я наполегливо рекомендую вам почати з [Processing](https://processing.org/) і повернутися пізніше, коли освоїтесь.
Ця книга навчить вас використовувати та інтегрувати шейдери у ваші проєкти, покращуючи їхню продуктивність і графічну якість. Оскільки GLSL (OpenGL Shading Language) шейдери компілюються і працюють на різних платформах, ви зможете застосувати отримані тут знання до будь-якого середовища, яке використовує OpenGL, OpenGL ES або WebGL. Іншими словами, ви зможете застосовувати та використовувати свої знання у [Processing](https://processing.org/)-скетчах, [openFrameworks](http://openframeworks.cc/) програмах, інтерактивних інсталяціях [Cinder](http ://libcinder.org/), веб-сайтах з [Three.js](http://threejs.org/) або iOS/Android іграх.
## Про що йдеться в цій книзі?
Ця книга буде присвячена використанню піксельних GLSL шейдерів. Спочатку ми визначимо, що таке шейдери, а потім навчимося створювати за їх допомогою процедурні форми, візерунки, текстури та анімацію. Ви вивчите основи мови шейдерів та застосуєте її до більш корисних задач, таких як: обробка зображень (різні операції із зображеннями, згортання матриці, розмиття, кольорові фільтри, таблиці пошуку та інші ефекти) і симуляції ("гра життя" Конвея, реакційно-дифузійна модель Грея-Скотта, брижі води, акварельні ефекти, комірки Вороного тощо). Ближче до кінця книги ми побачимо набір передових технік, заснованих на Ray Marching (алгоритми трасування променів).
*У кожному розділі є інтерактивні приклади, з кодом яких можна взаємодіяти.* При редагуванні коду ви одразу побачите відповідні зміни. Описані поняття можуть бути абстрактними та незрозумілими, тому інтерактивні приклади будуть корисними при вивченні матеріалу. Чим швидше ви побачите концепції в дії, тим легшим буде процес навчання.
Що не розглядається в цій книзі:
* Це *не* книга з openGL або webGL. OpenGL/webGL є більш обширною темою, ніж GLSL або фрагментні шейдери. Щоб дізнатися більше про openGL/webGL, я рекомендую переглянути: [OpenGL Introduction](https://open.gl/introduction), [the 8th edition of the OpenGL Programming Guide](http://www.amazon.com/OpenGL-Programming-Guide-Official-Learning/dp/0321773039/ref=sr_1_1?s=books&ie=UTF8&qid=1424007417&sr=1-1&keywords=open+gl+programming+guide) (також відома як червона книга) або [WebGL: Up and Running](http://www.amazon.com/WebGL-Up-Running-Tony-Parisi/dp/144932357X/ref=sr_1_4?s=books&ie=UTF8&qid=1425147254&sr=1-4&keywords=webgl)
* Це *не* підручник з математики. Хоча ми розглянемо низку алгоритмів і методів, які спираються на розуміння алгебри та тригонометрії, ми не будемо пояснювати їх докладно. Якщо у вас виникнуть запитання щодо математики, я рекомендую тримати під рукою одну з таких книг як: [3rd Edition of Mathematics for 3D Game Programming and computer Graphics](http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&qid=1424007839&sr=8-1&keywords=mathematics+for+games) або [2nd Edition of Essential Mathematics for Games and Interactive Applications](http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&qid=1424007889&sr=8-1&keywords=essentials+mathematics+for+developers).
## Що потрібно для початку?
Не багато! Якщо у вас є сучасний браузер, який підтримує WebGL (наприклад, Chrome, Firefox або Safari) і підключення до Інтернету, просто натисніть кнопку «Next» в кінці цієї сторінки.
Окрім того, в залежності від вашого бажання, ви можете:
- [Зробити офлайн-версію книги](https://thebookofshaders.com/appendix/00/?lan=ua)
- [Запустити приклади на Raspberry Pi без браузера](https://thebookofshaders.com/appendix/01/?lan=ua)
- [Зібрати PDF-версію книги для друку](https://thebookofshaders.com/appendix/02/?lan=ua)
- Переглянути [GitHub-репозиторій книги](https://github.com/patriciogonzalezvivo/thebookofshaders) для виправлення помилок або поділитися своїми прикладами коду.

@ -18,7 +18,7 @@ In the following chapters you will discover how incredibly fast and powerful thi
This book is written for creative coders, game developers and engineers who have coding experience, a basic knowledge of linear algebra and trigonometry, and who want to take their work to an exciting new level of graphical quality. (If you want to learn how to code, I highly recommend you start with [Processing](https://processing.org/) and come back later when you are comfortable with it.)
This book will teach you how to use and integrate shaders into your projects, improving their performance and graphical quality. Because GLSL (OpenGL Shading Language) shaders compile and run on a variety of platforms, you will be able to apply what you learn here to any enviroment that uses OpenGL, OpenGL ES or WebGL. In other words, you will be able to apply and use your knowledge with [Processing](https://processing.org/) sketches, [openFrameworks](http://openframeworks.cc/) applications, [Cinder](http://libcinder.org/) interactive installations, [Three.js](http://threejs.org/) websites or iOS/Android games.
This book will teach you how to use and integrate shaders into your projects, improving their performance and graphical quality. Because GLSL (OpenGL Shading Language) shaders compile and run on a variety of platforms, you will be able to apply what you learn here to any environment that uses OpenGL, OpenGL ES or WebGL. In other words, you will be able to apply and use your knowledge with [Processing](https://processing.org/) sketches, [openFrameworks](http://openframeworks.cc/) applications, [Cinder](http://libcinder.org/) interactive installations, [Three.js](http://threejs.org/) websites or iOS/Android games.
## What does this book cover?

@ -0,0 +1,48 @@
# Вступ
## Що таке фрагментний шейдер?
У попередньому розділі ми описали шейдери як еквівалент пресу Гутенберга для графіки. Чому? І що ще важливіше: що таке шейдер?
![Ліворуч: літера за літерою (монах скрипторію за роботою, William Blades, 1891). Праворуч: Сторінка за сторінкою, (печатний станок, Rolt-Wheeler, 1920.](print.png)
Якщо у вас уже є досвід малювання за допомогою комп'ютера, то ви знаєте, що в цьому процесі ви, умовно кажучи, спочатку малюєте коло, потім прямокутник, лінію, ще кілька трикутників і так далі, доки не отримаєте потрібне зображення. Цей процес дуже схожий на написання листа чи книги від руки це набір інструкцій, які послідовно виконують одне завдання за іншим.
Шейдери також є набором інструкцій, але вони виконуються одночасно для кожного пікселя на екрані. Це означає, що код, який ви пишете, має поводити себе по-різному залежно від положення пікселя на екрані. Подібно до печатного преса, ваша програма працюватиме як функція, яка отримує координати пікселя та повертає колір. Після компіляції вона працюватиме надзвичайно швидко.
![Китайський набірний шрифт](typepress.jpg)
## Чому шейдери швидкі?
Щоб відповісти на це питання, я розповім про чудеса *паралельних обчислень*.
Уявіть центральний процесор вашого комп'ютера у вигляді великої промислової труби, а кожне завдання як щось, що проходить крізь неї, як через фабричну лінію. Деякі завдання більші за інші, а це означає, що для їх вирішення потрібно більше часу та енергії. В комп'ютерному сенсі ми скажемо, що їм потрібна більша обчислювальна потужність. Через особливості архітектури комп'ютерів завдання виконуються послідовно, тобто по черзі. Сучасні комп'ютери зазвичай мають групи з кількох процесорів, які працюють як ці труби, виконуючи завдання одне за одним, щоб забезпечити безперебійну роботу. Кожна подібна труба також називається *потоком*.
![CPU](00.jpeg)
Відеоігри та інші графічні програми вимагають набагато більшої обчислювальної потужності, ніж інші програми. Через свій графічний контент їм доводиться виконувати величезну кількість попіксельних операцій. Необхідно обчислити кожен окремий піксель на екрані, а в 3D-іграх також потрібно обчислити усю геометрію і перспективу.
Повернемося до нашої метафори про труби та завдання. Кожен піксель на екрані представляє просте маленьке завдання. Окремо такі завдання не є проблемою для CPU, але проблема в тому, що кожне крихітне завдання потрібно виконати для кожного пікселя на екрані! Це означає, що на старому екрані з роздільною здатністю 800x600 потрібно обробляти 480 000 пікселів на 1 кадр оновлення, що становить близько 14 400 000 обчислень за секунду! О, так! Це достатньо велика проблема, щоб перенавантажити мікропроцесор. У сучасному дисплеї Retina 2880x1800, що оновлюється зі швидкістю 60 кадрів на секунду, ця кількість обчислень за секунду складе 311 040 000. Як інженери графічних систем розв'язують цю проблему?
![](03.jpeg)
Ось коли паралельні обчислення стають хорошим рішенням. Замість того, щоб мати пару великих і потужних мікропроцесорів, або *труб*, розумніше мати багато маленьких мікропроцесорів, що працюють паралельно. Саме так і влаштовано графічний процесор (GPU).
![GPU](04.jpeg)
Уявіть крихітні мікропроцесори у вигляді столу із труб, а дані кожного пікселя як кульку для пінг-понгу. 14 400 000 кульок для пінг-понгу за секунду можуть закупорити майже будь-яку трубу. Але стіл з крихітними трубками розміром 800x600, зможе спокійно прийняти 30 хвиль по 480 000 пікселів за секунду і працюватиме безперебійно. Те ж саме і з прикладом вищої роздільної здатності - чим більше у вас обладнання, що працює паралельно, тим більшим потоком воно зможе керувати.
Ще одна "суперздатність" графічного процесора — це спеціальні математичні функції, прискорені за допомогою апаратного забезпечення. Тож складні математичні операції вирішуються безпосередньо мікрочіпами, а не програмним забезпеченням. Це означає надшвидкі тригонометричні та матричні операції - настільки швидкі, наскільки швидко може рухатися електрика.
## Що таке GLSL?
GLSL розшифровується як OpenGL Shading Language і є спеціальним стандартом шейдерних програм, які ви побачите в наступних розділах. Залежно від апаратного забезпечення та операційної системи існують різні типи шейдерів. Ми працюватимемо зі специфікаціями OpenGL, які регулює [Khronos Group](https://www.khronos.org/opengl/). Розуміння історії OpenGL може бути корисним для розуміння більшості його дивних конвенцій. Для цього я рекомендую переглянути наступне посилання: [openglbook.com/chapter-0-preface-what-is-opengl.html](http://openglbook.com/chapter-0-preface-what-is-opengl.html)
## Чому шейдери такі болючі?
Як сказав дядько Бен, "з великою силою приходить велика відповідальність" і паралельні обчислення дотримуються цього правила. Потужний архітектурний дизайн графічного процесора має власні обмеження.
Щоб працювати паралельно, кожен канал або потік має бути незалежним від будь-якого іншого потоку. Ми кажемо, що потоки *сліпі* стосовно того, що роблять інші потоки. Це обмеження означає, що всі дані повинні рухатись в одному напрямку. Тому неможливо перевірити результат іншого потоку, змінити вхідні дані або передати результат одного потоку в інший потік. Спроба дозволу міжпотокових зв'язків ставить під загрозу цілісність даних.
Крім того, GPU підтримує свої мікропроцесори (труби) постійно зайнятими. Як тільки вони звільняються, вони отримують нову інформацію для обробки. Потоку неможливо дізнатися, що він робив у попередній момент. Це могло бути малювання кнопки інтерфейсу операційної системи, потім рендеринг частини неба із гри, а потім зображення тексту електронного листа. Кожен потік є не лише **сліпим**, а й **безпам'ятним**. Окрім абстракції, необхідної для кодування загальної функції, яка змінює результат в залежності від положення пікселя, сліпота і безпам'ятство роблять шейдери не дуже популярними серед програмістів-початківців.
Не хвилюйтесь! У наступних розділах ми крок за кроком розглянемо шейдерні обчислення від простого до складного. Якщо ви читаєте книгу в сучасному браузері, то оціните можливість взаємодії з інтерактивними прикладами. Тож давайте більше не відкладати веселощі та натискайте на посилання *Next >>*, щоб перейти до програмування!

@ -0,0 +1,53 @@
## Hello World
Зазвичай програма "Hello world!" — це перший крок до вивчення нової мови. Це проста програма, яка виводить вітальне повідомлення та заявляє про майбутні можливості.
У GPU-світі рендеринг тексту є надскладним завданням для першого кроку. Натомість щоб висловити свій ентузіазм, ми виберемо та намалюємо яскравий колір!
<div class="codeAndCanvas" data="hello_world.frag"></div>
Якщо ви читаєте цю книгу в браузері, попередній блок коду є інтерактивним. Це означає, що ви можете змінити будь-яку частину коду, яку захочете. Зміни будуть оновлені негайно завдяки архітектурі GPU, яка компілює та замінює шейдери *на льоту*. Спробуйте змінити значення в рядку 8.
Хоча ці прості рядки коду не виглядають складними, ми можемо вивести з них суттєві знання:
1. Мова шейдерів має одну функцію `main`, яка повертає колір у кінці. Це схоже на мову C.
2. Остаточний колір пікселя записується у зарезервовану глобальну змінну `gl_FragColor`.
3. У цій C-подібній мові є вбудовані *змінні* (наприклад, `gl_FragColor`), *функції* і *типи*. У цьому випадку ми щойно познайомилися з `vec4`, який означає чотиривимірний вектор числових значень з рухомою крапкою. Пізніше ми побачимо більше типів, таких як `vec3` і `vec2` разом із популярними `float`, `int` і `bool`.
4. Якщо ми уважно подивимося на тип `vec4`, то можемо зробити висновок, що його чотири аргументи відповідають ЧЕРВОНОМУ, ЗЕЛЕНОМУ, СИНЬОМУ та АЛЬФА-каналам. Також ми бачимо, що ці значення *нормалізовані* - це означає, що вони знаходяться в діапазоні від `0.0` до `1.0`. Пізніше ми дізнаємося, як нормалізація полегшує *зіставлення* значень між змінними.
5. Інша важлива *C-особливість*, яку ми можемо побачити в цьому прикладі, вказує на наявність макросів препроцесора. Макроси є частиною етапу попередньої компіляції. З їх допомогою можна визначити (`#define`) глобальні змінні та виконати деякі базові умовні операції, використовуючи `#ifdef` і `#endif`. Усі макрокоманди починаються з символу решітки `#`. Пре-компіляція відбувається безпосередньо перед компіляцією, підставляє всі визначення із директив `#defines` і перевіряє умови `#ifdef` (якщо визначено) і `#ifndef` (якщо не визначено). У наведеному вище прикладі "hello world!", ми вставляємо рядок 2, лише якщо визначено `GL_ES`, що здебільшого відбувається, коли код компілюється на мобільних пристроях і в браузерах.
6. Типи чисел з рухомою крапкою життєво важливі в шейдерах, тому рівень *точності* є вирішальним. Нижча точність означає швидший рендеринг, але ціною якості. Ви можете бути вибагливими та вказати точність кожної такої змінної, яку використовує. У другому рядку (`precision mediump float;`) ми встановлюємо середню точність для всіх значень з рухомою крапкою. Також можна вибрати низьку (`precision lowp float;`) або високу (`precision highp float;`) точність.
7. Остання і, мабуть, найважливіша деталь полягає в тому, що специфікації GLSL не гарантують автоматичного приведення типів. Що це означає? Виробники мають різні підходи до прискорення роботи відеокарт, але вони змушені гарантувати мінімальні вимоги. Автоматичне приведення типів не відноситься до них. У нашому прикладі `vec4` має містити число з рухомою крапкою на яке й очікує. Якщо ви хочете писати якісний узгоджений код і не витрачати години на відладку білих екранів, звикніть ставити крапку (`.`) для значень з рухомою крапкою. Бо наступний код працюватиме не завжди та не скрізь:
```glsl
void main() {
gl_FragColor = vec4(1, 0, 0, 1); // ПОМИЛКА
}
```
Тепер, коли ми описали найбільш релевантні елементи нашої програми "hello world!", настав час повернутися до блоку з кодом та почати застосовувати отримані знання. Ви помітите, що в разі помилок програма не компілюється, показуючи білий екран. Є кілька речей, які можна спробувати, наприклад:
* Спробуйте замінити числа з рухомою крапкою на цілі числа. Ваша графічна карта може підтримувати або не підтримувати таку поведінку.
* Спробуйте закоментувати рядок 8 і не призначати пікселю жодного значення.
* Спробуйте створити окрему функцію, яка повертає певний колір, і використайте її всередині `main()`. Нижче приклад функції, яка повертає червоний колір:
```glsl
vec4 red() {
return vec4(1.0, 0.0, 0.0, 1.0);
}
```
* Існує кілька способів конструювання типу `vec4`, спробуйте знайти інші способи. Ось один з них:
```glsl
vec4 color = vec4(vec3(1.0, 0.0, 1.0), 1.0);
```
Це не дуже захопливий, але найпростіший приклад - ми змінюємо всі пікселі всередині полотна на той самий однаковий колір. У наступному розділі ми побачимо, як змінити кольори пікселів за допомогою двох типів вхідних даних: просторового (положення пікселя на екрані) і часового (кількість секунд після завантаження сторінки).

@ -0,0 +1,61 @@
## Uniforms
Раніше ми побачили, яким чином графічний процесор керує великою кількістю паралельних потоків, кожен з яких відповідає за призначення кольору частці загального зображення. Хоча кожен паралельний потік закритий для інших, ми повинні мати можливість надсилати деякі вхідні дані від CPU до всіх потоків. Через архітектуру відеокарти ці вхідні дані будуть однаковими (*uniform* - *уніфікованими*, *однорідними*) для всіх потоків і доступними *лише для читання*. Іншими словами, кожен потік отримує ті ж самі дані, які він може читати, але не може змінювати.
Ці вхідні дані називаються `uniform` та мають більшість підтримуваних типів: `float`, `vec2`, `vec3`, `vec4`, `mat2`, `mat3`, `mat4`, `sampler2D` і `samplerCube `. Uniform-змінні визначаються з відповідним типом у верхній частині шейдера відразу після встановлення точності для float-значень.
```glsl
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution; // Розмір полотна (ширина, висота)
uniform vec2 u_mouse; // Положення курсору на екрані
uniform float u_time; // Час у секундах з моменту запуску коду
```
Ви можете уявити uniform-змінні як маленькі містки між CPU та GPU. Назви для змінних можуть відрізнятися від реалізації до реалізації, але в цій серії прикладів я завжди передаю: `u_time` (час у секундах із моменту запуску шейдера), `u_resolution` (розмір зображення де застосовується шейдер) і `u_mouse` (положення курсору всередині зображення, що вимірюється у пікселях). Я дотримуюся конвенції з префіксом `u_` перед назвою uniform-змінних, щоб чітко означити природу таких змінних, але насправді ви можете зустріти різні назви. Наприклад, [ShaderToy.com](https://www.shadertoy.com/) використовує ті ж самі змінні, але з наступними назвами:
```glsl
uniform vec3 iResolution; // роздільна здатність області зображення, у пікселях (viewport resolution)
uniform vec4 iMouse; // піксельні координати курсору. xy - поточні, zw - клік
uniform float iTime; // час роботи шейдера (у секундах)
```
Досить розмов, подивимося на уніформи в дії. У наведеному нижче коді ми використовуємо `u_time` — кількість секунд із моменту запуску шейдера — разом із синус-функцією, щоб анімувати зміни у кількості червоного кольору на екрані.
<div class="codeAndCanvas" data="time.frag"></div>
Як ви бачите, GLSL має ще багато сюрпризів. GPU має апаратне прискорення для кутових, тригонометричних та експоненціальних функцій. Ось деякі з цих функцій: [`sin()`](../glossary/?lan=ua&search=sin), [`cos()`](../glossary/?lan=ua&search=cos), [`tan()`](../glossary/?lan=ua&search=tan), [`asin()`](../glossary/?lan=ua&search=asin), [`acos()`](../glossary/?lan=ua&search=acos), [`atan()`](../glossary/?lan=ua&search=atan), [`pow()`](../glossary/?lan=ua&search=pow), [`exp()`](../glossary/?lan=ua&search=exp), [`log()`](../glossary/?lan=ua&search=log), [`sqrt()`](../glossary/?lan=ua&search=sqrt), [`abs()`](../glossary/?lan=ua&search=abs), [`sign()`](../glossary/?lan=ua&search=sign), [`floor()`](../glossary/?lan=ua&search=floor), [`ceil()`](../glossary/?lan=ua&search=ceil), [`fract()`](../glossary/?lan=ua&search=fract), [`mod()`](../glossary/?lan=ua&search=mod), [`min()`](../glossary/?lan=ua&search=min), [`max()`](../glossary/?search=max) and [`clamp()`](../glossary/?lan=ua&search=clamp).
Настав час знову пограти з наведеним вище кодом.
* Уповільнюйте частоту, щоб зміна кольору стала майже непомітною.
* Пришвидшуйте, поки не побачите один колір без мерехтіння.
* Пограйтеся із RGB-каналами та різними частотами для них, щоб отримати якусь цікаву поведінку.
## gl_FragCoord
Подібно до того як GLSL, за замовчуванням, дає нам вихідні дані `vec4 gl_FragColor`, він також дає нам вхідні дані за замовчуванням, `vec4 gl_FragCoord`, що містять екранні координати *пікселя* або *фрагменту екрана*, з якими працює активний потік. За допомогою `vec4 gl_FragCoord` ми знаємо, де саме працює поточний потік всередині зображення. У цьому випадку ми не називаємо його `uniform`, тому що він буде відрізнятися від потоку до потоку, натомість `gl_FragCoord` називається *varying* (змінливим).
<div class="codeAndCanvas" data="space.frag"></div>
У наведеному вище коді ми *нормалізуємо* координати фрагмента, розділивши їх на роздільну здатність зображення. У результаті цього отримані значення будуть змінюватися у діапазоні від `0.0` до `1.0`, що полегшує зіставлення X і Y значень з ЧЕРВОНИМ і ЗЕЛЕНИМ каналами.
У світі шейдерів ми не маємо зручних інструментів для зневаджування програми, тому інколи доводиться призначати змінним якісь яскраві кольори, щоб за їх допомогою спробувати дістати потрібну інформацію. Ви виявите, що кодування на GLSL іноді дуже схоже на розміщення корабля у пляшку. Це однаково важко, красиво і захопливо.
![](08.png)
Тепер настав час перевірити наше розуміння цього коду.
* Чи можете ви сказати, де знаходиться координата `(0.0, 0.0)` на нашому полотні?
* А як щодо `(1.0, 0.0)`, `(0.0, 1.0)`, `(0.5, 0.5)` і `(1.0, 1.0)`?
* Чи можете ви зрозуміти, як використати змінну `u_mouse`, знаючи, що її значення вказані в пікселях і НЕ нормалізовані? Чи зможете за її допомогою і руху курсору змінювати кольори?
* Чи можете ви вигадати цікавий спосіб для зміни кольорів за допомогою координат `u_time` і `u_mouse`?
Після виконання цих вправ ви можете поставити собі питання: де ще можна спробувати можливості шейдерів. У наступному розділі ми розглянемо, як створити власні шейдерні інструменти у three.js, Processing і openFrameworks.

@ -0,0 +1,233 @@
## Запуск шейдера
У процесі створення цієї книги та моєї художньої практики, я сформував екосистему інструментів для створення шейдерів, їх відображення, спільного використання та керування. Ці інструменти працюють однаково в Linux, MacOS, Windows, [Raspberry Pi](https://www.raspberrypi.org/) та браузерах без необхідності змінювати код.
## Запуск шейдерів у браузері
**Відображення**: усі інтерактивні приклади в цій книзі відображаються за допомогою [glslCanvas](https://github.com/patriciogonzalezvivo/glslCanvas), що робить процес запуску автономного шейдера неймовірно простим.
```html
<canvas class="glslCanvas" data-fragment-url=“yourShader.frag" data-textures=“yourInputImage.png” width="500" height="500"></canvas>
```
Як бачите, для цього потрібен лише елемент `canvas` із відповідним класом `class="glslCanvas"` та URL-адресою вашого шейдера, заданої в атрибуті `data-fragment-url`. Дізнатися більше про це можна [тут](https://github.com/patriciogonzalezvivo/glslCanvas).
Якщо ви схожі на мене, ви, ймовірно, захочете запускати шейдери безпосередньо з консолі. У такому випадку вам допоможе [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer). Ця програма дозволяє вам включати шейдери у ваші `bash`-скрипти або unix-конвеєри та використовувати їх подібно до [ImageMagick](http://www.imagemagick.org/script/index.php). Також [glslViewer](https://github.com/patriciogonzalezvivo/glslViewer) є чудовим способом компіляції шейдерів на [Raspberry Pi](https://www.raspberrypi.org/), через що [openFrame.io](http://openframe.io/) використовує його для демонстрації шейдерних робіт. Дізнайтеся більше про цю програму за цим [посиланням](https://github.com/patriciogonzalezvivo/glslViewer).
```bash
glslViewer yourShader.frag yourInputImage.png —w 500 -h 500 -E screenshot,yourOutputImage.png
```
**Створення**: щоб покращити досвід кодування шейдерів, я створив онлайн-редактор під назвою [glslEditor](https://github.com/patriciogonzalezvivo/glslEditor). Цей редактор використовується в інтерактивних прикладах книги та пропонує низку зручних віджетів, що робить взаємодію з абстрактним кодом glsl більш відчутним. Ви також можете запустити його як окрему вебпрограму: [editor.thebookofshaders.com](http://editor.thebookofshaders.com/). Дізнатися більше про редактор можна [тут](https://github.com/patriciogonzalezvivo/glslEditor).
![](glslEditor-01.gif)
Якщо ви бажаєте працювати з редактором офлайн за допомогою [SublimeText](https://www.sublimetext.com/), то можете встановити цей [пакет для glslViewer](https://packagecontrol.io/packages/glslViewer). Дізнайтеся більше [тут](https://github.com/patriciogonzalezvivo/sublime-glslViewer).
![](glslViewer.gif)
**Експортування**: онлайн-редактор ([editor.thebookofshaders.com](http://editor.thebookofshaders.com/)) може поділитися вашими шейдерами! Як вбудована, так і окрема версія мають кнопку для експорту, за допомогою якої ви можете отримати унікальну URL-адресу свого шейдера. Також є можливість експортувати шейдер безпосередньо в [openFrame.io](http://openframe.io/).
![](glslEditor-00.gif)
**Керування**: експорт коду — початок для того, щоб ви ділилися своїм шейдером у якості художнього витвору! Окрім опції експорту в [openFrame.io](http://openframe.io/), я створив інструмент для керування вашими шейдерами у галереї, яку можна вбудувати у будь-який сайт — [glslGallery](https://github.com/patriciogonzalezvivo/glslGallery). Детальніше [тут](https://github.com/patriciogonzalezvivo/glslGallery).
![](glslGallery.gif)
## Запуск ваших шейдерів у вашому улюбленому фреймворку
Якщо у вас уже є досвід програмування в таких фреймворках, як [Processing](https://processing.org/), [Three.js](http://threejs.org/), [OpenFrameworks](http://openframeworks.cc/) або [SFML](https://www.sfml-dev.org/), ви, мабуть, із задоволенням спробуєте шейдери у них. Нижче наведено приклади того, як налаштувати шейдери в деяких популярних фреймворках з вказаними у книзі uniform-змінними. У [GitHub-репозиторії для цього розділу](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04) ви знайдете повний вихідний код для цих трьох фреймворків.
### **Three.js**
Блискучий і дуже скромний Ricardo Cabello (він же [MrDoob](https://twitter.com/mrdoob)) з групою інших [однодумців](https://github.com/mrdoob/three.js/graphs/contributors) розробили один із найвідоміших фреймворків для WebGL під назвою [Three.js](http://threejs.org/). Там ви знайдете безліч прикладів, посібників і книг, які навчать вас, як використовувати цю JS-бібліотеку для створення класної 3D-графіки.
Нижче наведено приклад із HTML та JS, які необхідні для початку роботи з шейдерами у three.js. Зверніть увагу на скрипт з `id="fragmentShader"` куди ви можете скопіювати шейдери, знайдені в цій книзі.
```html
<body>
<div id="container"></div>
<script src="js/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
gl_Position = vec4(position, 1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
gl_FragColor = vec4(st.x, st.y, 0.0, 1.0);
}
</script>
<script>
let container;
let camera, scene, renderer, clock;
let uniforms;
init();
animate();
function init() {
container = document.getElementById('container');
camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();
clock = new THREE.Clock();
const geometry = new THREE.PlaneBufferGeometry(2, 2);
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_mouse: { type: "v2", value: new THREE.Vector2() }
};
const material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
document.onmousemove = function(e) {
uniforms.u_mouse.value.x = e.pageX
uniforms.u_mouse.value.y = e.pageY
}
}
function onWindowResize(event) {
renderer.setSize(window.innerWidth, window.innerHeight);
uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
uniforms.u_time.value += clock.getDelta();
renderer.render(scene, camera);
}
</script>
</body>
```
### **Processing**
[Processing](https://processing.org/) започатковано у 2001 році у співпраці між [Ben Fry](http://benfry.com/) та [Casey Reas](http://reas.com/). Фреймворк є надзвичайно простим та потужним середовищем, у якому можна робити свої перші кроки в програмуванні (принаймні так було у мене). [Andres Colubri](https://codeanticode.wordpress.com/) вніс важливі оновлення в Processing для підтримки openGL і відео, що полегшило використання шейдерів GLSL у цьому дружньому середовищі, ніж будь-коли. Processing шукатиме шейдер із назвою `"shader.frag"` у теці `data` вашого скетчу. Тож вам достатньо скопіювати приклади цієї книги у файл із відповідною назвою.
```cpp
PShader shader;
void setup() {
size(640, 360, P2D);
noStroke();
shader = loadShader("shader.frag");
}
void draw() {
shader.set("u_resolution", float(width), float(height));
shader.set("u_mouse", float(mouseX), float(mouseY));
shader.set("u_time", millis() / 1000.0);
shader(shader);
rect(0,0,width,height);
}
```
Щоб шейдер працював у попередніх версіях, нижчих за 2.1, вам потрібно додати рядок `#define PROCESSING_COLOR_SHADER` на початку вашого шейдера. Це виглядатиме так:
```glsl
#ifdef GL_ES
precision mediump float;
#endif
#define PROCESSING_COLOR_SHADER
uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.st / u_resolution;
gl_FragColor = vec4(st.x, st.y, 0.0, 1.0);
}
```
Щоб дізнатися більше про шейдери у Processing, перегляньте цей [посібник](https://processing.org/tutorials/pshader/).
### **openFrameworks**
У кожного є місце, де йому комфортно, у моєму випадку таким місцем залишається [спільнота openFrameworks](http://openframeworks.cc/). Цей C++ фреймворк є обгорткою навколо OpenGL та інших бібліотек на C++ з відкритим кодом. Багато в чому він дуже схожий на Processing, але зі своїми особливостями роботи з використанням компіляторів C++. Так само як і Processing, openFrameworks шукатиме ваші файли шейдерів у теці `data`. Тому не забудьте скопіювати код прикладів, які хочете використати, у файли з розширенням `.frag` і покласти їх у згадану теку:
```cpp
void ofApp::draw() {
ofShader shader;
shader.load("", "shader.frag");
shader.begin();
shader.setUniform1f("u_time", ofGetElapsedTimef());
shader.setUniform2f("u_resolution", ofGetWidth(), ofGetHeight());
ofRect(0, 0, ofGetWidth(), ofGetHeight());
shader.end();
}
```
Якщо ви хочете використовувати в OpenFrameworks повний набір уніформ, що містяться в специфікаціях GlslViewer і GlslCanvas, простішим способом, я рекомендую використовувати доповнення [ofxShader](https://github.com/patriciogonzalezvivo/ofxshader). Воно також має підтримку кількох буферів, шейдерних матеріалів, гаряче перезавантаження та автоматизацію для OpenGL ES у Raspberry Pi. Ваш код буде таким же простим, як нижче:
```cpp
//--------------------------------------------------------------
void ofApp::setup(){
ofDisableArbTex();
sandbox.allocate(ofGetWidth(), ofGetHeight());
sandbox.load("grayscott.frag");
}
//--------------------------------------------------------------
void ofApp::draw(){
sandbox.render();
sandbox.draw(0, 0);
}
```
Щоб дізнатися більше про шейдери в openFrameworks, перегляньте цей [чудовий посібник](http://openframeworks.cc/ofBook/chapters/shaders.html), створений [Joshua Noble](http://thefactoryfactory.com/).
### **Blender**
[GlslTexture](https://github.com/patriciogonzalezvivo/glslTexture) — це доповнення, яке дозволяє програмно генерувати текстури за допомогою шейдерів GLSL і повністю сумісне з рештою прикладів даного розділу. Як це працює:
1. Ввімкніть пошук: `F3` або `пробіл`, залежно від налаштувань. Введіть `GlslTexture`:
![](blender/00.png)
2. Змініть значення розмірів для полів `width`, `height` та `Source`-шлях до зовнішнього файлу, якщо такий є.
![](blender/01.png)
3. Використовуйте зображення у своїх матеріалах. Ім’я зображення базуватиметься на назві вихідного файлу.
![](blender/02.png)
4. Перейдіть до текстового редактора (або зовнішнього редактора, якщо ваш вихідний файл також зовнішній) і відредагуйте шейдер. Після чого відбудеться оновлення результату.
![](blender/03.png)

@ -190,7 +190,7 @@ void ofApp::draw(){
}
```
If you want to use the full set of uniforms contain on the specs of GlslViewer and GlslCanvas in a more simple way on OpenFrameworks I recomend using the [ofxShader](https://github.com/patriciogonzalezvivo/ofxshader) addon which will also have support for multiple buffers, material shaders, hotreload and automatic conversion for OpenGL ES in the Raspberry Pi. And your code will be as simple as doing
If you want to use the full set of uniforms contain on the specs of GlslViewer and GlslCanvas in a more simple way on OpenFrameworks I recommend using the [ofxShader](https://github.com/patriciogonzalezvivo/ofxshader) addon which will also have support for multiple buffers, material shaders, hotreload and automatic conversion for OpenGL ES in the Raspberry Pi. And your code will be as simple as doing
```cpp
//--------------------------------------------------------------

@ -0,0 +1,134 @@
# Алгоритмічне малювання
## Формотворчі функції або функції формування
Цей розділ можна було б назвати "Урок з парканом від містера Міягі". Раніше ми зіставляли нормалізоване положення координат *x* і *y* з *червоним* і *зеленим* каналами. По суті, ми створили функцію, яка приймає двовимірний вектор (x і y) та повертає чотиривимірний вектор (r, g, b і a). Але перш ніж перейти до подальшого перетворення даних між вимірами, нам потрібно почати з простіших речей... набагато простіших. З розуміння того, як користуватись одновимірними функціями. Чим більше часу та енергії ви витратите на освоєння цього і відповідну практику, тим сильнішим буде ваше шейдерне карате.
![The Karate Kid (1984)](mr_miyagi.jpg)
Наступний приклад коду буде нашим парканом. У ньому ми візуалізуємо нормалізоване значення координати *x* (`st.x`) двома способами: один за допомогою яскравості (подивіться на гарний градієнт від чорного до білого), а інший шляхом побудови зеленої діагональної лінії зверху (у цьому випадку значення *x* присвоюється безпосередньо до *y*). Поки що не зосереджуйтеся занадто на функції `plot`. Згодом ми розберемося з нею більш детально.
<div class="codeAndCanvas" data="linear.frag"></div>
**Коротка примітка**: конструктор типу `vec3` "розуміє", що ви хочете призначити одне і те саме значення трьом каналам, тоді як `vec4` розуміє, що ви хочете побудувати чотиривимірний вектор за допомогою одного тривимірного і додаткового четвертого значення. У цьому випадку четверте значення відповідатиме за альфа-канал або непрозорість. Перегляньте ці приклади на рядках 19 і 25.
Цей код — ваш паркан. Важливо бачити його і розуміти. Ви знову і знову повертатиметеся до цього простору між *0.0* і *1.0*. Ви опануєте мистецтво змішування та формування подібних ліній.
Цей однозначний зв'язок між *x* і *y* (або яскравістю) відомий як *лінійна інтерполяція*. З цього моменту ми можемо використовувати деякі математичні функції для надання лінії певної *форми*, для її формування. Наприклад, ми можемо піднести *x* до 5 степеня, щоб отримати *криву* лінію.
<div class="codeAndCanvas" data="expo.frag"></div>
Цікаво, правда? У рядку 22 спробуйте різні показники для степеня: наприклад, 20.0, 2.0, 1.0, 0.0, 0.2 і 0.02. Розуміння цього зв'язку між значенням і експонентою буде дуже корисним. Використання подібних типів математичних функцій дасть вам виразний засіб для контролю вашого коду, свого роду акупунктуру даних, яка дозволить вам контролювати потік значень.
[`pow()`](../glossary/?lan=ua&search=pow) — одна з багатьох вбудованих функцій GLSL. Більшість із них прискорені на апаратному рівні, а це означає, що якщо вони використовуються належним чином і з обережністю, то ваш код стане швидшим.
Замініть функцію `pow` у рядку 22 на якусь іншу, наприклад: [`exp()`](../glossary/?lan=ua&search=exp), [`log()`](../glossary/?lan=ua&search=log) і [`sqrt()`](../glossary/?lan=ua&search=sqrt). Деякі з цих функцій стають цікавіші із використанням числа PI. У рядку 8 ви можете побачити, що я визначив макрос, який замінить будь-яке використання `PI` на значення `3.14159265359`.
### step і smoothstep
GLSL має деякі унікальні функції інтерполяції, які також апаратно прискорені.
Функція інтерполяції [`step()`](../glossary/?lan=ua&search=step) приймає два параметри. Перший — це межа або поріг, а другий — це значення для якого ми хочемо застосувати функцію. Будь-яке значення нижче порогу поверне `0.0`, а все, що його перевищує — `1.0`.
Спробуйте змінити у наступному коді порогове значення, що на рядку 20:
<div class="codeAndCanvas" data="step.frag"></div>
Інша унікальна функція називається [`smoothstep()`](../glossary/?lan=ua&search=smoothstep). Функція плавно інтерполює значення у вказаному діапазоні з двох чисел. Перші два параметри призначені для початку та кінця перехідного діапазону, а третій — для значення, яке потрібно інтерполювати.
<div class="codeAndCanvas" data="smoothstep.frag"></div>
У функції `plot()` на рядку 12 попереднього прикладу, ми використали smoothstep, щоб намалювати зелену лінію. Для кожної позиції вздовж вісі *x* ця функція робить *виступи* у певному значенні *y*. Яким чином? Об'єднавши результат двох функцій [`smoothstep()`](../glossary/?lan=ua&search=smoothstep) разом. Погляньте на наступну функцію, замініть нею рядок 20 вище і подумайте про неї як про вертикальний розріз. Фон схожий на лінію, чи не так?
```glsl
float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
```
### Синус і Косинус
Коли ви хочете застосувати певну математику для анімації, або при формуванні чи змішуванні значень, немає нічого кращого, ніж товаришувати з синусом і косинусом.
Ці дві основні тригонометричні функції працюють у парі при побудові кола, що є настільки ж корисним, як і швейцарський армійський ніж. Важливо знати, як вони поводяться і як їх можна комбінувати. Коротко кажучи, задаючи їм кут у радіанах, вони повертають правильне положення координати *x* ([cos](../glossary/?lan=ua&search=cos)) і *y* ([sin](../glossary/?lan=ua&search=sin)) точки на краю кола з радіусом, рівним 1. А той факт, що вони повертають нормалізовані значення (від -1 до 1), які до того ж достатньо плавні, робить їх неймовірним інструментом.
![](sincos.gif)
Хоча важко описати всі взаємозв'язки між тригонометричними функціями та колами, наведена вище анімація чудово їх узагальнює.
<div class="simpleFunction" data="y = sin(x);"></div>
Уважно подивіться на зображену вище синусоїду. Зверніть увагу, як значення *y* плавно змінюються між +1 і -1. Як ми бачили у прикладі зі змінною для часу, що був у попередньому розділі, ви можете використовувати цю ритмічну поведінку [`sin()`](../glossary/?lan=ua&search=sin) для анімації властивостей. Якщо ви читаєте цей приклад у браузері, то можете змінити код у формулі вище, щоб побачити, як змінюється хвиля. (Примітка: не забудьте про крапку з комою в кінці рядка.)
Спробуйте виконати наступні вправи та зверніть увагу на те, що відбувається:
* Додайте час (`u_time`) до *x* перед обчисленням `sin`. Оцініть **рух** уздовж осі *x*.
* Помножте *x* на `PI` перед обчисленням `sin`. Зверніть увагу, як дві фази **скорочуються**, так що кожен цикл повторюється через кожні 2 цілих числа.
* Помножте час (`u_time`) на *x* перед обчисленням `sin`. Подивіться, як **частота** між фазами стає все більш і більш стиснутою. Зверніть увагу, що `u_time` до цього моменту може стати вже дуже великим, що ускладнить сприйняття отриманого графіка.
* Додайте 1.0 до [`sin(x)`](../glossary/?lan=ua&search=sin). Подивіться, як уся хвиля **змістилася** вгору, і тепер усі значення знаходяться між 0.0 і 2.0.
* Помножте [`sin(x)`](../glossary/?lan=ua&search=sin) на 2.0. Подивіться, як **амплітуда** збільшилася вдвічі.
* Обчисліть абсолютне значення за допомогою ([`abs()`](../glossary/?lan=ua&search=abs)) `sin(x)`. Графік стане схожим на траєкторію м'яча, що **стрибає**.
* Виділіть лише дробову частину від результату [`sin(x)`](../glossary/?lan=ua&search=sin) за допомогою [`fract()`](../glossary/?lan=ua&search=fract).
* Додайте результати [`sin(x)`](../glossary/?lan=ua&search=sin), округлені в більшу ([`ceil()`](../glossary/?lan=ua&search=ceil)) та меншу сторони ([`floor()`](../glossary/?lan=ua&search=floor)), щоб отримати цифрову хвилю зі значеннями 1 і -1.
### Деякі додаткові корисні функції
Наприкінці останньої вправи ми представили кілька нових функцій. Настав час поекспериментувати з ними. Спробуйте по черзі розкоментувати та випробувати кожну з них. Ознайомтеся з цими функціями та вивчіть, як вони поводяться. Я знаю, вам цікаво... навіщо? Швидкий пошук у Google на тему "генеративне мистецтво" підкаже вам. Майте на увазі, що ці функції є нашим парканом. Ми опановуємо рух в одному вимірі, вгору і вниз. Вже зовсім скоро настане час для двох, трьох і чотирьох вимірів!
![Anthony Mattox (2009)](anthony-mattox-ribbon.jpg)
<div class="simpleFunction" data="y = mod(x,0.5); // return x modulo of 0.5
// y = fract(x); // повертає лише дробову частину числа
// y = ceil(x); // найближче ціле число, яке більше або дорівнює x
// y = floor(x); // найближче ціле число, яке менше або дорівнює x
// y = sign(x); // отримати знак числа x
// y = abs(x); // повертає абсолютне значення x
// y = clamp(x,0.0,1.0); // обмеження значення x в діапазоні між 0.0 і 1.0
// y = min(0.0,x); // повертає менше значення із x і 0.0
// y = max(0.0,x); // повертає більше значення із x і 0.0 "></div>
### Просунуті формотворчі функції
[Golan Levin](http://www.flong.com/) має чудову документацію про складніші формотворчі функції, які є надзвичайно корисними. Перенесення їх на GLSL — це розумний крок, щоб почати створювати власний ресурс з фрагментами коду.
* Поліноміальні формотворчі функції: [www.flong.com/archive/texts/code/shapers_poly](http://www.flong.com/archive/texts/code/shapers_poly/)
* Експоненціальні формотворчі функції: [www.flong.com/archive/texts/code/shapers_exp](http://www.flong.com/archive/texts/code/shapers_exp/)
* Кругові та еліптичні формотворчі функції: [www.flong.com/archive/texts/code/shapers_circ](http://www.flong.com/archive/texts/code/shapers_circ/)
* Безьє та інші параметричні функції: [www.flong.com/archive/texts/code/shapers_bez](http://www.flong.com/archive/texts/code/shapers_bez/)
<div class="glslGallery" data="160414041542,160414041933,160414041756" data-properties="clickRun:editor,hoverPreview:false"></div>
Подібно до кухарів, які збирають спеції та екзотичні інгредієнти, цифрові художники та креативні кодери приділяють особливу увагу створенню власних формотворчих функцій.
[Iñigo Quiles](http://www.iquilezles.org/) має чудову колекцію [корисних функцій](http://www.iquilezles.org/www/articles/functions/functions.htm). Прочитавши [цю статтю](http://www.iquilezles.org/www/articles/functions/functions.htm), перегляньте на наступні реалізації цих функцій на GLSL. Зверніть увагу на невеликі зміни, які потрібно було внести. Як-от на "." (крапку) для чисел з рухомою крапкою та заміни деяких *C*-функцій на GLSL аналоги: наприклад, замість `powf()` використовується `pow()`.
<div class="glslGallery" data="05/impulse,05/cubicpulse,05/expo,05/expstep,05/parabola,05/pcurve" data-properties="clickRun:editor,hoverPreview:false"></div>
Для підтримки вашої мотивації, ось елегантний приклад опанування карате формотворчих функцій. Автор [Danguafer](https://www.shadertoy.com/user/Danguafer):
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/XsXXDn?gui=true&t=10&paused=true" allowfullscreen></iframe>
У наступному розділі ми почнемо використовувати нові карате-рухи. Спочатку почнемо зі змішування кольорів, а потім перейдемо до малювання фігур.
#### Вправа
Подивіться на наведену нижче таблицю рівнянь, створену за авторства [Kynd](http://www.kynd.info/log/). Подивіться, як він поєднує функції та їхні властивості, щоб контролювати значення між 0.0 та 1.0. Настав час потренуватися, самостійно відтворюючи ці функції. Пам'ятайте, що чим більше ви тренуєтеся, тим краще буде ваше карате.
![Kynd - www.flickr.com/photos/kynd/9546075099/ (2013)](kynd.png)
#### До вашого інструментарію
Ось кілька інструментів, які полегшать візуалізацію цих типів функцій:
* [GraphToy](http://www.iquilezles.org/apps/graphtoy/): вже згаданий [Iñigo Quilez](http://www.iquilezles.org) створив інструмент для візуалізації GLSL-функцій у WebGL.
![Iñigo Quilez - GraphToy (2010)](graphtoy.png)
* [Бібліотека шейдерів LYGIA](https://lygia.xyz/) — бібліотека шейдерів із функцій, які можна легко включити та використати у ваших проєктах. Бібліотека дуже атомарна, створена для зручного перевикористання, продуктивності й гнучкості. Може бути легко додана до будь-яких проєктів та фреймворків.

@ -0,0 +1,136 @@
![Paul Klee - Color Chart (1931)](klee.jpg)
## Кольори
Раніше у нас не було нагоди поговорити про векторні типи GLSL. Перш ніж рушити далі, важливо дізнатися про них більше. А тема кольорів якраз дуже підходить для цього і буде чудовим шляхом для такого знайомства.
Якщо ви знайомі з парадигмами об'єктноорієнтованого програмування, то, мабуть, помітили, що ми зверталися до даних у векторах, як до будь-якої звичайної C-подібної `структури`.
```glsl
vec3 red = vec3(1.0, 0.0, 0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
```
Визначення кольору за допомогою нотації *x*, *y* і *z* може заплутати й ввести в оману, чи не так? Ось чому існують й інші способи доступу до цієї самої інформації, але під іншими іменами. Значення `.x`, `.y` і `.z` так само можна отримати через `.r`, `.g` і `.b`, а також через `.s`, `.t` і `.p`. (`.s`, `.t` і `.p` зазвичай використовуються для роботи з просторовими координатами текстури, які ми побачимо в наступному розділі.) Крім того, ви також можете отримати доступ до елементів вектора через позицію індексу: `[0]`, `[1]` і `[2]`.
Наступні рядки показують всі способи доступу до тих самих даних:
```glsl
vec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
```
Всі ці різні способи посилання на змінні всередині вектора — лише додаткові номенклатури — розроблені позначення, щоб допомогти вам написати чіткіший код. Ця гнучкість, вбудована в мову шейдерів, дає вам можливість почати думати про просторові координати та значення кольорів як про взаємозамінні сутності.
Ще одна чудова особливість векторних типів у GLSL полягає в тому, що їх властивості можна комбінувати в будь-якому довільному порядку, що спрощує приведення та змішування значень. Ця здатність називається *змішуванням*.
```glsl
vec3 yellow, magenta, green;
// Створення жовтого кольору
yellow.rg = vec2(1.0); // Присвоєння 1.0 червоному та зеленому каналам
yellow[2] = 0.0; // Присвоєння 0.0 синьому каналу
// Створення пурпурового кольору
magenta = yellow.rbg; // Присвоєння каналів зі зміною місць зеленого і синього. rbg замість rgb
// Створення зеленого кольору
green.rgb = yellow.bgb; // Присвоєння значення синього каналу зі змінної "yellow" червоному і синьому каналам змінної "red"
```
### Змішування кольорів
Тепер, коли ви знаєте, як визначаються кольори, настав час об'єднати ці нові знання з попередніми. У GLSL є дуже корисна функція [`mix()`](../glossary/?lan=ua&search=mix), яка дозволяє змішувати два значення у відсотках. Чи можете ви здогадатися, який саме там діапазон? Так, звісно це значення від 0.0 до 1.0! Ідеально підійде для вас, особливо після довгих годин відпрацювання рухів карате з парканом настав час скористатися ними!
![](mix-f.jpg)
Перевірте наступний код у рядку 18 і подивіться, як ми використовуємо абсолютні значення синусу на основі часу, щоб змішати значення кольорів у змінних `colorA` та `colorB`.
<div class="codeAndCanvas" data="mix.frag"></div>
Покажіть свої навички:
* Зробіть експресивний перехід між кольорами. Подумайте про якусь емоцію. Який колір здається найбільш характерним для неї? Як він з'являється? Як зникає? Подумайте про іншу емоцію та відповідний їй колір. Змініть початковий і кінцевий кольори у коді вище, відповідно цим емоціям. Потім анімуйте перехід за допомогою формотворчих функцій. Robert Penner розробив серію популярних функцій для комп'ютерної анімації, відомих як [функції пом'якшення](http://easings.net/). Ви можете використати [цей приклад](../edit.php#06/easing.frag) для дослідження та натхнення, але найкращий результат ви здобудете, налаштувавши свої власні переходи.
### Гра з градієнтами
Функція [`mix()`](../glossary/?lan=ua&search=mix) може запропонувати більше. Замість одного `float` значення ми можемо передати компонентний тип змінної, однаковий для перших двох аргументів, у нашому випадку це `vec3`. Таким чином ми отримуємо контроль над відсотками змішування кожного окремого каналу кольорів: `r`, `g` і `b`.
![](mix-vec.jpg)
Розгляньте наступний приклад. Як і в прикладах попереднього розділу, ми створюємо нормалізовані координати та використовуємо *x* для візуалізації лінії. Зараз усі канали змінюються по спільному правилу.
Тепер розкоментуйте рядок 25 і подивіться, що станеться. Потім спробуйте розкоментувати рядки 26 і 27. Пам’ятайте, що лінії візуалізують вагу змішування кожного каналу змінних `colorA` та `colorB`.
<div class="codeAndCanvas" data="gradient.frag"></div>
Ви, напевно, впізнали три функції, які ми використовуємо в рядках 25-27. Пограйте з ними! Настав час дослідити та продемонструвати свої навички з попереднього розділу та створити цікаві градієнти. Спробуйте наступні вправи:
![Вільям Тернер - Останній рейс "Тімірера" (1838)](turner.jpg)
* Створіть градієнт, що нагадує захід сонця Вільяма Тернера
* Анімуйте перехід між сходом і заходом сонця за допомогою `u_time`.
* Чи можете ви зробити веселку, використовуючи отримані до цього часу знання?
* Використайте функцію `step()`, щоб створити кольоровий прапор.
### HSB
Ми не можемо говорити про колір, не згадавши про колірний простір. Як ви, напевно, знаєте, існують різні способи організації кольорів, окрім червоного, зеленого та синього каналів.
[HSB](http://en.wikipedia.org/wiki/HSL_and_HSV) означає відтінок (Hue), насиченість (Saturation) і яскравість (Brightness або Value) і є більш інтуїтивно зрозумілою організацією кольорів. Прочитайте код функцій `rgb2hsv()` і `hsv2rgb()` у наступному прикладі.
Зіставляючи `x`-координату на відтінок та `y`-координату на яскравість, ми отримуємо гарний спектр видимих кольорів. Такий просторовий розподіл кольору може бути дуже зручним. Вибір кольору з простору HSB більш інтуїтивний, ніж з RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
### HSB в полярних координатах
HSB початково розроблений для представлення в полярних координатах (на основі кута та радіуса) замість декартових координат (на основі x і y). Щоб зіставити нашу функцію HSB з полярними координатами, нам потрібно отримати кут і відстань від центру полотна до піксельної координати. Для цього ми використаємо функцію розрахунку відстані — [`length()`](../glossary/?lan=ua&search=length) і арктангенс — [`atan(y, x)`](../glossary/?lan=ua&search=atan) (GLSL-версія загальновживаної функції `atan2(y, x)`).
Під час використання векторних і тригонометричних функцій типи `vec2`, `vec3` і `vec4` розглядаються як вектори, навіть якщо вони представляють кольори. Ми почнемо обробляти кольори та вектори однаковим чином. Згодом ви побачите, що ця концептуальна гнучкість дуже розширює ваші можливості.
**Примітка:** Якщо вам цікаво, то окрім [`length`](../glossary/?lan=ua&search=length) існують й інші геометричні функції, наприклад: [`distance()`](../glossary/?lan=ua&search=distance), [`dot()`](../glossary/?lan=ua&search=dot), [`cross()`](../glossary/?lan=ua&search=cross), [`normalize()`](../glossary/?lan=ua&search=normalize), [`faceforward()`](../glossary/?lan=ua&search=faceforward), [`reflect()`](../glossary/?lan=ua&search=reflect) та [`refract()`](../glossary/?lan=ua&search=refract). Також GLSL має спеціальні векторні функції порівняння, такі як: [`lessThan()`](../glossary/?lan=ua&search=lessThan), [`lessThanEqual()`](../glossary/?lan=ua&search=lessThanEqual), [`greaterThan()`](../glossary/?lan=ua&search=greaterThan), [`greaterThanEqual()`](../glossary/?lan=ua&search=greaterThanEqual), [`equal()`](../glossary/?lan=ua&search=equal) та [`notEqual()`](../glossary/?lan=ua&search=notEqual).
Отримавши кут і довжину, нам потрібно "нормалізувати" їх значення у діапазоні від 0.0 до 1.0. У рядку 27 [`atan(y, x)`](../glossary/?lan=ua&search=atan) поверне кут у радіанах між -PI та PI (від -3.14 до 3.14). Тому нам потрібно розділити це число на подвійне PI, яке записане у макрос `TWO_PI`, що визначений у верхній частині коду. Це дозволить нам отримати значення від -0.5 до 0.5, які шляхом простого додавання до них значення 0.5 ми змінимо на бажаний діапазон від 0.0 до 1.0. Радіус поверне максимум 0.5 (оскільки ми обчислюємо відстань від центру вікна перегляду), тому нам потрібно подвоїти цей діапазон (множенням на два), щоб отримати максимум 1.0.
Як бачите, наша гра полягає в перетворенні та масштабуванні значень, які нам потрібні у діапазоні від 0.0 до 1.0.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Спробуйте наступні вправи:
* Змініть приклад з полярними координатами, щоб отримати колірне коло, що обертається, як невелике коло біля екранного курсора в режимі очікування.
* Використайте функцію формування разом із функцією перетворення кольору з HSB у RGB, щоб розширити область кола з певним відтінком та звузити решту.
![William Home Lizars - Red, blue and yellow spectra, with the solar spectrum (1834)](spectrums.jpg)
* Якщо ви уважно придивитесь на колірне коло у програмних палітрах кольорів (див. зображення нижче), ви побачите, що вони використовують інший спектр відповідно до колірного простору RYB. Наприклад, колір, напроти червоного повинен бути зеленим, але в нашому прикладі вище це блакитний. Спробуйте змінити код прикладу, щоб отримати таке саме коло, як на наступному зображенні? [Підказка: це чудовий момент для використання функцій формування.]
![](colorwheel.png)
* Прочитайте книгу [Джозефа Альберса "Взаємодія кольору"](http://www.goodreads.com/book/show/111113.Interaction_of_Color) і скористайтеся для практики наведеними нижче прикладами шейдерів.
<div class="glslGallery" data="160505191155,160505193939,160505200330,160509131554,160509131509,160509131420,160509131240" data-properties="clickRun:editor,openFrameIcon:false,showAuthor:false"></div>
#### Примітка про функції та аргументи
Перш ніж перейти до наступного розділу, зупинімось і трохи перемотаємо назад. Поверніться та погляньте на функції в попередніх прикладах. Ви можете помітити слово `in` перед типом аргументів. Це [*кваліфікатор*](http://www.shaderific.com/glsl-qualifiers/#inputqualifier), і в цьому випадку він вказує, що змінна лише для читання. У наступних прикладах ми побачимо, що також можна визначити аргументи із кваліфікаторами `out` або `inout`. Цей останній, `inout`, концептуально подібний до передачі аргументу за посиланням, що дає нам можливість змінювати передану змінну.
```glsl
int newFunction(
in vec4 aVec4, // лише для читання
out vec3 aVec3, // лише для запису
inout int aInt // і для читання і для запису
);
```
Ви можете не повірити, але тепер у нас є всі необхідні елементи для створення крутих зображень. У наступному розділі ми навчимося комбінувати всі ці трюки, щоб створити геометричні фігури шляхом *змішування* простору. Так-так... *змішування* простору!

@ -0,0 +1,233 @@
![Alice Hubbard, Providence, United States, ca. 1892. Photo: Zindman/Freemont.](froebel.jpg)
## Фігури
Нарешті! Ми розвивали попередні навички саме для цього моменту! Ви вивчили більшість основ GLSL мови, її типи та функції. Ви знову й знову практикували свої навички у формотворчих функціях та рівняннях. Настав час зібрати все разом. Ви вже готові прийняти цей виклик! У цьому розділі ви дізнаєтесь, як малювати прості фігури за допомогою паралельних обчислень.
### Прямокутник
Уявіть, що ми маємо листок паперу у клітинку, як на уроках математики, і наше домашнє завдання — намалювати квадрат. Розмір паперу 10х10, а квадрат має бути 8х8. Що ви будете робити?
![](grid_paper.jpg)
Мабуть, ви б розфарбували все, окрім першого й останнього рядків та першого й останнього стовпців, чи не так?
Як це пов'язано з шейдерами? Кожен маленький квадрат нашої сітки — це один потік (піксель). Кожен квадратик знає своє положення, наче координати на шаховій дошці. У попередніх розділах ми масштабували координати *x* та *y* у канали *червоного* та *зеленого* кольорів й навчилися використовувати вузьку двовимірну територію масштабовану між 0.0 та 1.0. Як за допомогою цього ми можемо намалювати квадрат відцентрований посередині нашого полотна?
Почнімо з псевдокоду та використання `if`-операторів для просторових координат. Ці принципи надзвичайно схожі на випадок з паперовим варіантом.
```glsl
if ((X GREATER THAN 1) AND (Y GREATER THAN 1)) // якщо (x більше 1) та (y більше 1)
paint white // фарбуємо білим
else // інакше
paint black // фарбуємо чорним
```
Тепер, коли ми маємо краще уявлення про потрібну роботу, замінімо оператор `if` на [`step()`](../glossary/?lan=ua&search=step), а замість використання листка 10x10 використаємо нормалізовані значення між 0.0 і 1.0:
```glsl
uniform vec2 u_resolution;
void main(){
vec2 st = gl_FragCoord.xy / u_resolution.xy;
vec3 color = vec3(0.0);
// кожен результат поверне 1.0 (білий) або 0.0 (чорний).
float left = step(0.1, st.x); // подібно до (X більше 0.1)
float bottom = step(0.1, st.y); // подібно до (Y більше 0.1)
// множення left на bottom спрацює як логічний оператор AND.
color = vec3(left * bottom);
gl_FragColor = vec4(color, 1.0);
}
```
Функція [`step()`](../glossary/?lan=ua&search=step) перетворить кожен піксель нижче 0.1 на чорний колір (`vec3(0.0)`), а решту — на білий (`vec3(1.0)`). Множення між `left` і `bottom` працює як логічна операція AND, де обидва значення мають бути рівні 1.0, щоб повернути 1.0. Цей код намалює дві чорні лінії: знизу та зліва.
![](rect-01.jpg)
У попередньому коді ми повторюємо одну і ту ж саму дію для обох сторін: лівої та нижньої. Ми можемо зекономити кілька рядків коду, передавши у [`step()`](../glossary/?lan=ua&search=step) одразу два значення, у вигляді вектору, замість одного:
```glsl
vec2 borders = step(vec2(0.1), st);
float pct = borders.x * borders.y;
```
Поки що ми намалювали лише дві межі нашого прямокутника: нижню та ліву. Зробімо інші дві: верхню та праву. Розгляньте наступний код:
<div class="codeAndCanvas" data="rect-making.frag"></div>
Розкомментуйте *рядки 21-22* і подивіться, як ми інвертуємо там координати `st` і повторюємо ту саму функцію [`step()`](../glossary/?lan=ua&search=step). Таким чином `vec2(0.0, 0.0)` буде у верхньому правому куті. Це цифровий еквівалент перевороту сторінки з повторенням попередньої процедури.
![](rect-02.jpg)
Зверніть увагу, що в *рядках 18 і 22* всі сторони перемножуються разом. Це еквівалентно наступному написанню:
```glsl
vec2 bl = step(vec2(0.1), st); // нижній-лівий
vec2 tr = step(vec2(0.1), 1.0 - st); // верхніх-правий
color = vec3(bl.x * bl.y * tr.x * tr.y);
```
Цікаво правда? Ця техніка базується на використанні [`step()`](../glossary/?lan=ua&search=step), множенні у якості логічної операції та перевороті координат.
Перш ніж продовжити, спробуйте виконати такі вправи:
* Змініть розмір і пропорції прямокутника.
* Поекспериментуйте з тим самим кодом, використовуючи [`smoothstep()`](../glossary/?lan=ua&search=smoothstep) замість [`step()`](../glossary/?lan=ua&search=step). Зауважте, що змінюючи значення, ви можете переходити від розмитих меж до елегантно згладжених країв.
* Виконайте іншу реалізацію, з використанням [`floor()`](../glossary/?lan=ua&search=floor).
* Виберіть реалізацію, яка вам найбільше до вподоби, та створіть для неї функцію, яку ви зможете повторно використовувати в майбутньому. Зробіть свою функцію гнучкою та ефективною.
* Створіть іншу функцію, яка малюватиме просто контур прямокутника.
* Як можна було б пересувати та розміщувати різні прямокутники на одному полотні? Якщо зрозумієте як це зробити, продемонструйте свої вміння, створивши композицію з прямокутників і кольорів, що нагадує картину [Piet Mondrian](http://en.wikipedia.org/wiki/Piet_Mondrian).
![Piet Mondrian - Tableau (1921)](mondrian.jpg)
### Кола
Легко намалювати квадрати на папері в клітинку та прямокутники за декартовими координатами, але кола вимагають іншого підходу, особливо тому, що нам потрібен "по-піксельний" алгоритм. Одне з рішень полягає в *перетворенні* просторових координат таким чином, щоб ми змогли використовувати функцію [`step()`](../glossary/?lan=ua&search=step) для малювання кола.
Як? Почнімо з того, що повернемося до уроку математики та паперу у клітинку. Розкриємо циркуль на радіус кола, поставимо його голку в центрі кола, а потім окреслимо окружність кола простим обертанням.
![](compass.jpg)
Перекладаючи це на мову шейдера, де кожен квадрат на папері є пікселем, ми маємо *спитати* кожен піксель (або потік), чи знаходиться він усередині кола. Ми робимо це, обчислюючи відстань від пікселя до центру кола.
![](circle.jpg)
Є кілька способів розрахувати цю відстань. Найпростіший використовує функцію [`distance()`](../glossary/?lan=ua&search=distance), яка всередині обчислює [`length()`](../glossary/?lan=ua&search=length)-різниці між двома точками. У нашому випадку між координатою пікселя та центром полотна. По своїй суті функція `length()` — це не що інше, як скорочення для [рівняння визначення довжини гіпотенузи](http://en.wikipedia.org/wiki/Hypotenuse), яке використовує квадратний корінь ([`sqrt()`](../glossary/?lan=ua&search=sqrt)).
![](hypotenuse.png)
Щоб обчислити відстань до центру полотна ви можете використовувати [`distance()`](../glossary/?lan=ua&search=distance), [`length()`](../glossary/?lan=ua&search=length) або [`sqrt()`](../glossary/?lan=ua&search=sqrt). Наступний код містить ці три функції та той недивний факт, що кожна з них повертає точно такий самий результат.
* Закоментуйте та розкоментуйте рядки, щоб спробувати різними способами отримати той самий результат.
<div class="codeAndCanvas" data="circle-making.frag"></div>
У попередньому прикладі ми переводимо відстань від центру полотна у яскравість кольору пікселя. Чим ближче піксель до центру, тим менше (темніше) значення він має. Зауважте, що значення не стають надто високими, оскільки максимальна відстань від центру (`vec2(0.5, 0.5)`) ледве перевищує значення 0.5. Подивіться на зображення і подумайте:
* Який висновок ви можете з цього зробити?
* Як ми можемо використати це, щоб намалювати коло?
* Змініть наведений вище код, щоб вмістити весь круговий градієнт всередині полотна.
### Поле відстаней
Наведений вище приклад можна розглянути як карту висот, де темніший колір означає вищу позицію. В такому разі цей круговий градієнт буде картою конуса. Уявіть себе на вершині цього конуса. Відстань по горизонталі до краю конуса, в будь-якому напрямку, дорівнює 0.5. Вибираючи, на якій висоті "зрізати" конус, ви отримаєте більший чи менший діаметр кругової поверхні.
![](distance-field.jpg)
По суті ми інтерпретуємо простір на основі його відстані до центру для того, щоб робити таким чином фігури. Ця техніка відома як "поле відстаней" і використовується різними шляхами, від контурів шрифтів до 3D-графіки.
Спробуйте наступні вправи:
* Використовуйте [`step()`](../glossary/?lan=ua&search=step), щоб перетворити значення більші за 0.5 у білий колір, а все, що менше 0.0 — у чорний.
* Змініть місцями кольори фону та переднього плану.
* Використовуючи [`smoothstep()`](../glossary/?lan=ua&search=smoothstep) та експериментуючи з різними значеннями, отримайте гарний плавний край для вашого кола.
* Для найбільш вдалої вашої реалізації створіть функцію, яку ви зможете повторно використовувати в майбутньому.
* Додайте колу колір.
* Чи зможете ви анімувати своє коло, щоб воно збільшувалося та зменшувалося, імітуючи серцебиття? Ви можете отримати натхнення з анімації у попередньому розділі.
* А як щодо переміщення кола? Чи зможете ви перемістити та розташувати на полотні кілька різних кіл?
* Що станеться, якщо скомбінувати поля відстаней за допомогою різних функцій та операцій?
```glsl
pct = distance(st, vec2(0.4)) + distance(st, vec2(0.6));
pct = distance(st, vec2(0.4)) * distance(st, vec2(0.6));
pct = min(distance(st, vec2(0.4)),distance(st, vec2(0.6)));
pct = max(distance(st, vec2(0.4)),distance(st, vec2(0.6)));
pct = pow(distance(st, vec2(0.4)),distance(st, vec2(0.6)));
```
* Створіть три композиції за допомоги цієї техніки. Ще краще, якщо вони будуть анімовані!
#### Для вашого інструментарію
З точки зору обчислювальної потужності, функція [`sqrt()`](../glossary/?lan=ua&search=sqrt) і всі функції, які від неї залежать, можуть бути досить ресурсовитратними. Ось ще один спосіб для створення кругового поля відстаней за допомогою функції [`dot()`](../glossary/?lan=ua&search=dot).
<div class="codeAndCanvas" data="circle.frag"></div>
### Корисні властивості поля відстаней
![Zen garden](zen-garden.jpg)
Поля відстаней можна використовувати для малювання майже всього. Очевидно, що чим складнішою є форма, тим складнішим буде її рівняння. Але як тільки у вас є формула для створення поля відстаней певної форми, її дуже легко комбінувати та/або застосовувати до неї ефекти. Наприклад, згладження країв або численні контури. Через це поля відстаней популярні у цифровому зображенні шрифтів, наприклад [Mapbox GL Labels](https://blog.mapbox.com/drawing-text-with-signed-distance-fields-in-mapbox-gl-b0933af6f817), [Material Design Fonts](http://mattdesl.svbtle.com/material-design-on-the-gpu) (автор: [Matt DesLauriers](https://twitter.com/mattdesl)) та [iPhone 3D Programming, OReilly (див. розділ 7)](http://chimera.labs.oreilly.com/books/1234000001814/ch07.html#ch07_id36000921).
Розгляньте наступний код:
<div class="codeAndCanvas" data="rect-df.frag"></div>
Ми починаємо з переміщення відліку системи координат у центр. Звужуємо її вдвічі, щоб вона вміщувала в себе значення позиції між -1.0 та 1.0. Також у *рядку 24* ми візуалізуємо значення поля відстаней за допомогою функції [`fract()`](../glossary/?lan=ua&search=fract), що покаже малюнок форми, який воно створює. Контур поля відстаней повторюється знову і знову, як кільця в саду дзен.
Подивімось на формулу поля відстаней у *рядку 19*. Тут ми обчислюємо відстань до позиції у точці `(.3, .3)` (або `vec3(.3)`) в усіх чотирьох квадрантах (саме для цього було використано [`abs()`](../glossary/?lan=ua&search=abs)).
Якщо ви розкоментуєте *рядок 20*, то помітите, що ми об'єднуємо відстані до цих чотирьох точок за допомогою функції [`min()`](../glossary/?lan=ua&search=min) до нуля. У результаті виходить новий цікавий візерунок.
Тепер спробуйте розкоментувати *рядок 21*. Тут ми робимо те саме, але використовуємо функцію [`max()`](../glossary/?lan=ua&search=max). В результаті вийде прямокутник із закругленими кутами. Зверніть увагу, що кільця поля відстаней стають більш гладкими, чим далі вони віддаляються від центру.
Нарешті, по черзі розкоментуйте *рядки з 27 по 29*, щоб побачити та зрозуміти різні варіанти шаблонів використання поля відстаней.
### Фігури у полярних координатах
![Robert Mangold - Untitled (2008)](mangold.jpg)
У розділі про колір ми зіставляли декартові координати з полярними координатами, обчислюючи *радіус* і *кут* кожного пікселя за такою формулою:
```glsl
vec2 pos = vec2(0.5) - st;
float r = length(pos) * 2.0;
float a = atan(pos.y, pos.x);
```
На початку розділу ми використали частину цієї формули, щоб намалювати коло. Ми обчислили відстань до центру за допомогою функції [`length()`](../glossary/?lan=ua&search=length). Тепер, знаючи про поля відстаней, ми можемо навчитися малювати фігури за допомогою полярних координат іншим способом.
Ця техніка трохи обмежена, але дуже проста. Вона полягає в отримання різних форм, змінюючи радіус кола залежно від кута. Як це працює? Звісно ж за допомогою функцій формування!
Нижче ви знайдете функції для отримання значень у декартовій системі координат та зображення їх на графіку. А ще нижче інший приклад з тими ж самими функціями, але вже для полярних координат (між *рядками 21 і 25*). Розкоментуйте ці функції одну за одною та звертайте увагу на співвідношення між обома системами координат.
<div class="simpleFunction" data="y = cos(x * 3.0);
//y = abs(cos(x * 3.0));
//y = abs(cos(x * 2.5)) * 0.5 + 0.3;
//y = abs(cos(x * 12.) * sin(x * 3.0)) * .8 + .1;
//y = smoothstep(-0.5, 1.0, cos(x * 10.0)) * 0.2 + 0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Спробуйте:
* Анімувати ці форми.
* Комбінувати різні формотворчі функції, щоб *вирізати отвори* у формі й зробити подобу квітів, сніжинок та шестерень.
* Використати функцію `plot()` з розділу [Формотворчих функцій](/05/?lan=ua), щоб намалювати лише контур.
### Сила комбінацій
Ми навчилися модулювати радіус кола відповідно до кута за допомогою функції [`atan()`](../glossary/?lan=ua&search=atan) для малювання різних фігур. Тепер ми можемо навчитися використовувати `atan()` з полями відстаней та застосувати всі трюки та ефекти, можливі з цими полями.
Наступний трюк використовує кількість ребер багатокутника для побудови поля відстаней за допомогою полярних координат. Перегляньте [цей код](http://thndl.com/square-shaped-shaders.html) від [Andrew Baldwin](https://twitter.com/baldand).
<div class="codeAndCanvas" data="shapes.frag"></div>
* Використовуючи цей приклад, створіть функцію, яка приймає положення та кількість кутів потрібного багатокутника й повертає значення поля відстаней.
* Змішайте поля відстаней за допомогою функції [`min()`](../glossary/?lan=ua&search=min) та [`max()`](../glossary/?lan=ua&search=max).
* Виберіть геометричний логотип і відтворіть його за допомогою полей відстані.
Щиро вітаю! Ви пройшли через складну частину! Зробіть перерву й дайте цим концепціям засвоїтись. Так, малювати прості фігури десь у Processing легко, але не тут. У світі шейдерів малювання фігур хитромудра справа, і адаптація до цієї нової парадигми кодування може бути виснажливою.
У кінці цього розділу ви знайдете посилання на [Колоду PixelSpirit](https://patriciogonzalezvivo.github.io/PixelSpiritDeck/). Ця колода карт допоможе вам вивчити нові функції SDF, скомпонувати їх та використати у ваших шейдерах. Колода має прогресивну криву навчання. Можете брати по одній карті на день й опрацьовувати її, щоб підштовхнути та випробувати свої навички.
Тепер, коли ви знаєте, як малювати фігури, я впевнений, що у вас у голові з’являться нові ідеї. У наступному розділі ви дізнаєтесь, як переміщувати, обертати та масштабувати фігури. Це дозволить вам складати композиції!

@ -0,0 +1,105 @@
## Двомірні матриці
<canvas id="custom" class="canvas" data-fragment-url="matrix.frag" width="700px" height="200px"></canvas>
### Переміщення
У попередньому розділі ми навчилися створювати деякі фігури — хитрість переміщення цих фігур полягає в тому, щоб перемістити саму систему координат. Ми можемо досягти цього, просто додавши вектор до змінної ```st```, яка містить розташування кожного фрагмента. Це спричиняє зміщення у просторі усієї системи координат.
![](translate.jpg)
Це легше побачити, ніж пояснити, тому дивіться самі:
* Розкоментуйте рядок 35 в коді нижче, щоб побачити, як рухається сам простір.
<div class="codeAndCanvas" data="cross-translate.frag"></div>
Тепер спробуйте наступне:
* Використовуючи змінну ```u_time``` разом із функціями формування, надайде хрестику рух у новому цікавому напрямку. Знайдіть конкретний варіант руху, яка вас цікавить, і спробуйте змусити хрест рухатися таким же чином. Спробуйте для початку щось із "реального світу" — це можуть бути хвилеподібні рухи, коливання маятника, м’яч, що підстрибує, прискорення як в автомобіля чи зупинка велосипеда.
### Обертання
Щоб обертати об'єкти, нам також потрібно рухати всю систему простору. Для цього ми будемо використовувати [матрицю](http://en.wikipedia.org/wiki/Matrix_%28mathematics%29). Матриця — це організований набір чисел у стовпцях і рядках. Вектори помножуються на матриці відповідно до певних правил, щоб змінити значення вектора визначеним чином.
[![Wikipedia entry for Matrix (mathematics) ](matrixes.png)](https://en.wikipedia.org/wiki/Matrix)
GLSL має вбудовану підтримку дво-, три- та чотиривимірних матриць: [```mat2```](../glossary/?lan=ua&search=mat2) (2x2), [```mat3```](../glossary/?lan=ua&search=mat3) (3x3) та [```mat4```](../glossary/?lan=ua&search=mat4) (4x4). GLSL також підтримує множення матриць (```*```) та специфічну для матриці функцію ([```matrixCompMult()```](../glossary/?lan=ua&search=matrixCompMult)).
Можна побудувати різні матриці для отримання певної поведінки. Наприклад, ми можемо використати матрицю для переміщення вектора:
![](3dtransmat.png)
Що ще цікавіше, ми можемо використати матрицю для обертання системи координат:
![](rotmat.png)
Подивіться на наведений нижче код функції, яка створює двовимірну матрицю обертання. Ця функція відповідає наведеній вище [формулі](http://en.wikipedia.org/wiki/Rotation_matrix) для обертання координат навколо точки ```vec2(0.0)```.
```glsl
mat2 rotate2d(float _angle) {
return mat2(
cos(_angle), -sin(_angle),
sin(_angle), cos(_angle)
);
}
```
Судячи з того, як ми малювали фігури, це не зовсім те, що нам потрібно. Наша форма хреста намальована в центрі полотна, що відповідає позиції ```vec2(0.5)```. Отже, перед тим, як обертати простір, нам потрібно перемістити фігуру із `центру` до координати ```vec2(0.0)```, повернути простір, а потім, нарешті, перемістити його назад у початкове положення.
![](rotate.jpg)
Це можна запрограмувати таким чином:
<div class="codeAndCanvas" data="cross-rotate.frag"></div>
Спробуйте наступні вправи:
* Розкоментуйте рядок 45 із коду вище та зверніть увагу на те, що відбувається.
* Закоментуйте зміщення координат до та після повороту, у рядках 38 та 42, спостерігаючи за наслідками.
* Використайте обертання, щоб покращити анімацію, яку ви моделювали під час вправи з переміщенням.
### Масштаб
Ми побачили, як матриці використовуються для переміщення та обертання об'єктів у просторі. А точніше, як змінювати системи координат для обертання та переміщення об'єктів. Якщо ви користувалися програмним забезпеченням для 3D-моделювання або функціями `push` та `pop` для матриць у Processing, то знаєте, що матриці також можна використовувати й для масштабування розміру об'єктів.
![](scale.png)
Дотримуючись попередньої формули, ми можемо створити двомірну матрицю масштабування:
```glsl
mat2 scale(vec2 _scale) {
return mat2(
_scale.x, 0.0,
0.0, _scale.y
);
}
```
<div class="codeAndCanvas" data="cross-scale.frag"></div>
Спробуйте виконати наступні вправи, щоб глибше зрозуміти, як це працює.
* Розкомментуйте рядок 42 із коду вище, щоб побачити масштабування просторових координат.
* Подивіться, що відбудеться, коли ви закоментуєте переміщення координат до та після масштабування в рядках 37 та 39.
* Спробуйте поєднати матрицю обертання разом із матрицею масштабування. Майте на увазі, що порядок має значення. Спочатку помножте на матрицю, а потім результат помножте на вектори.
* Тепер, коли ви знаєте, як малювати різні фігури, переміщувати, обертати та масштабувати їх, настав час створити гарну композицію. Спроєктуйте та створіть фейковий [UI або HUD](https://www.pinterest.com/patriciogonzv/huds/). HUD (heads up display) — мається на увазі розташування інформації на прозорих поверхнях або дисплеях. Для натхнення та довідки, можете використати наступний ShaderToy-приклад від [Ndel](https://www.shadertoy.com/user/ndel).
<iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe>
### Інше використання матриць: колір у просторі YUV
[YUV](http://en.wikipedia.org/wiki/YUV) — це колірний простір, який використовується для аналогового кодування фотографій та відео, яке враховує діапазон людського сприйняття для зменшення пропускної здатності компонентів колірності.
Наступний код є цікавою можливістю використовувати матричні операції в GLSL для перетворення кольорів з одного простору в інший.
<div class="codeAndCanvas" data="yuv.frag"></div>
Як бачите, ми розглядаємо кольори як вектори, перемножуючи їх на матриці. Таким чином ми "переміщуємо" значення кольорів.
У цьому розділі ми навчилися використовувати матричні перетворення для переміщення, обертання та масштабування векторів. Ці трансформації будуть корисними для створення композицій із фігур, про які ми дізналися із попереднього розділу. У наступному розділі ми застосуємо всі здобуті знання для створення красивих процедурних візерунків. Ви побачите, що програмування повторень та варіацій може бути захопливою діяльністю.

@ -0,0 +1,115 @@
## Патерни
Шейдерні програми виконуються попіксельно, тому незалежно від того скільки ви повторюєте якусь фігуру, кількість обчислень залишається незмінною. Це означає, що фрагментні шейдери добре підходять для створення патернів — візерунків, що повторюються.
[![Nina Warmerdam - The IMPRINT Project (2013)](warmerdam.jpg)](../edit.php#09/dots5.frag)
У цьому розділі ми збираємося застосувати дещо з попереднього матеріалу, щоб продублювати його кілька разів на полотні. Як і в попередніх розділах, наша стратегія базуватиметься на перемноженні просторових координат з діапазоном від 0.0 до 1.0, так щоб фігури, які ми малюватимемо між значеннями 0.0 та 1.0, повторювалися, утворюючи сітку.
*"Сітка забезпечує структуру, в межах якої людській інтуїції та винахідливості зручно орієнтуватись. У хаосі природи візерунки створюють контраст і обіцяють порядок. Від ранніх візерунків на кераміці до геометричної мозаїки в римських лазнях люди давно використовують сітки, щоб прикрасити своє життя за допомогою декору."* [*10 PRINT*, Mit Press, (2013)](http://10print.org/)
Спочатку згадаймо функцію [```fract()```](../glossary/?lan=ua&search=fract). Вона повертає дробову частину числа, роблячи ```fract()``` модулем одиниці ([```mod(x,1.0)```](../glossary/?lan=ua&search=mod)). Іншими словами, [```fract()```](../glossary/?lan=ua&search=fract) повертає число після коми з рухомою крапкою. Наша змінна з нормалізованою системою координат (```st```) уже змінюється в межах діапазону від 0.0 до 1.0, тому немає сенсу робити щось на наступний зразок:
```glsl
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
vec3 color = vec3(0.0);
st = fract(st);
color = vec3(st, 0.0);
gl_FragColor = vec4(color, 1.0);
}
```
Але якщо ми збільшимо масштаб нормалізованої системи координат, скажімо, втричі до 3.0, то отримаємо три послідовності лінійної інтерполяції одиничного розміру. Перша між 0.0 та 1.0, друга між 1.0 та 2.0 й третя між 2.0 та 3.0.
<div class="codeAndCanvas" data="grid-making.frag"></div>
Настав час намалювати щось у кожному підпросторі, розкоментувавши рядок 27. Оскільки обидві вісі x та y помножуються на однакове значення, то співвідношення сторін простору не змінюється. Відповідно форми також не матимуть викривлень та будуть такими, як очікувалося.
Для глибшого розуміння, спробуйте виконати деякі з наведених нижче вправ:
* Помножте просторові координати на різні числа. Спробуйте різні значення з рухомою крапкою, а також різні значення для x та y.
* Зробіть, для цього трюку з плиткою, функцію для повторного використання.
* Розділіть простір на 3 рядки та 3 стовпці. Знайдіть спосіб дізнатися, у якому стовпці та рядку знаходиться потік, і в залежності від цього змінити фігуру, що буде малюватися. Спробуйте зробити композицію з гри у хрестики-нулики.
### Застосування матриць всередині патернів
Оскільки кожен підрозділ або комірка є зменшеною версією нормалізованої системи координат, яку ми використовували раніше, ми можемо застосувати до них такі ж самі матричні перетворення для переміщення, обертання та масштабування.
<div class="codeAndCanvas" data="checks.frag"></div>
* Подумайте про цікаві способи анімації цього візерунка. Подумайте про анімацію кольорів, форм і рухів. Зробіть три різні анімації.
* Відтворіть складніші візерунки, компонуючи різні форми.
[![](diamondtiles-long.png)](../edit.php#09/diamondtiles.frag)
* Комбінуйте різні шари патернів, щоб створити власні [орнаменти шотландського тартану](https://www.google.com/search?q=scottish+patterns+fabric&tbm=isch&tbo=u&source=univ&sa=X&ei=Y1aFVfmfD9P-yQTLuYCIDA&ved=0CB4QsAQ&biw=1399&bih=799#tbm=isch&q=Scottish+Tartans+Patterns).
[![Векторний візерунок шотландського тартану від Kavalenkava](tartan.jpg) ](http://graphicriver.net/item/vector-pattern-scottish-tartan/6590076)
### Патерни зі зміщенням
Припустимо, ми хочемо імітувати цегляну стіну. Розглядаючи стіну, ви можете побачити, що кожен другий її ряд зміщений на півцеглини по вісі x. Як можна це зробити?
![](brick.jpg)
Першим кроком потрібно визначити, чи є потік поточного рядка парним чи непарним, що дасть нам розуміння чи потрібно робити зміщення у цьому рядку. Для цього ми використаємо функцію модуля [```mod()```](../glossary/?lan=ua&search=mod) зі значенням ```2.0``` для другого параметру, а потім перевіримо, чи результат буде меншим від ``` 1.0``` чи ні. Подивіться на наступну формулу та розкоментуйте два останні рядки.
<div class="simpleFunction" data="y = mod(x, 2.0);
// y = mod(x, 2.0) < 1.0 ? 0. : 1. ;
// y = step(1.0, mod(x, 2.0));"></div>
Як бачите, ми можемо використати [тернарний оператор](https://en.wikipedia.org/wiki/%3F:), щоб перевірити, чи остача від ділення по модулю [```mod()```](../glossary/?lan=ua&search=mod) на ```2.0``` менше за ```1.0```, що означатиме другий рядок. Або ми можемо аналогічно використати функцію [```step()```](../glossary/?lan=ua&search=step), яка виконає ту саму операцію, але швидше. Чому? Хоча важко напевно знати, як кожна графічна карта оптимізує та компілює код, але можна з упевненістю припустити, що вбудовані функції працюють швидше. За кожної можливості надавайте перевагу використанню вбудованих функцій!
Тепер, маючи формулу визначення непарних чисел, ми можемо застосувати зміщення до непарних рядків, щоб надати нашим плиткам *ефект цегли*. У рядку 14 наступного коду ми використовуємо функцію для "виявлення" непарних рядків та зсуваємо їх на половину одиниці по вісі ```x```. Зауважте, що для парних рядків результатом нашої функції буде ```0.0```, що при множенні на значення для зміщення в ```0.5``` залишається тим самим ```0.0```. Але в непарних рядках ми отримуємо результат нашої функції як ```1.0```, що при множенні на зсув ```0.5```, переміщує вісь ```x``` системи координат на ```0.5```.
Тепер спробуйте розкоментувати рядок 32 — це розтягне співвідношення сторін системи координат, щоб імітувати її до співвідношення "сучасної цегли". Розкоментувавши рядок 40, ви зможете побачити, як виглядає система координат, відображена у червоний та зелений кольори.
<div class="codeAndCanvas" data="bricks.frag"></div>
* Спробуйте анімувати цей приклад, змінюючи зміщення відповідно до часу.
* Зробіть ще одну анімацію, у якій парні ряди рухаються ліворуч, а непарні — праворуч.
* Чи можете ви повторити цей ефект, але зі стовпцями?
* Спробуйте скомбінувати зсуви на вісі ```x``` та ```y```, щоб отримати щось на зразок наступного:
<a href="../edit.php#09/marching_dots.frag"><canvas id="custom" class="canvas" data-fragment-url="marching_dots.frag" width="520px" height="200px"></canvas></a>
## Плитка Труше
Тепер, коли ми навчилися визначати, чи знаходиться наша комірка в парному чи непарному рядку чи стовпці, можна повторно використовувати один елемент дизайну залежно від його положення. Розглянемо випадок [плитки Труше](http://en.wikipedia.org/wiki/Truchet_tiles), де один елемент дизайну може бути представлено чотирма різними способами:
![](truchet-00.png)
Змінюючи малюнок у плитках, можна побудувати нескінченний набір складних зображень.
![](truchet-01.png)
Зверніть увагу на функцію ```rotateTilePattern()```, яка розбиває простір на чотири комірки та призначає кожній кут повороту.
<div class="codeAndCanvas" data="truchet.frag"></div>
* Закоментуйте, розкоментуйте та продублюйте рядки з 69 по 72, щоб скомпонувати нові зображення.
* Змініть чорно-білий трикутник на інший елемент, наприклад: півколо, повернуті квадрати або лінії.
* Запрограмуйте інші патерни, де елементи обернені відповідно до їхнього положення.
* Створіть патерн, який змінює інші свої властивості відповідно до положення елементів.
* Подумайте про щось інше, не обов'язково про патерни, де ви можете застосувати принципи з цього розділу. Наприклад, [гексаграми "I Ching"](https://en.wikipedia.org/wiki/Hexagram_(I_Ching)):
<a href="../edit.php#09/iching-01.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-01.frag" width="520px" height="200px"></canvas></a>
## Створення власних правил
Створення процедурних патернів — це розумова вправа з пошуку мінімальної кількості елементів для багаторазово використання в просторі. Ця давня практика. Ми, як біологічний вид, використовуємо сітки та візерунки для декорування текстилю, підлоги та границь об'єктів протягом тривалого часу: від візерунків декоративних звивистих ліній (меандрів) стародавньої Греції до китайських решітчастих патернів. Насолода від повторення та варіацій захоплює нашу уяву. Знайдіть час, щоб переглянути [декоративні](https://archive.org/stream/traditionalmetho00chririch#page/130/mode/2up) [візерунки](https://www.pinterest.com/patriciogonzv/paterns/) та подивитися як художники з дизайнерами балансуються між передбачуваністю порядку й несподіваністю варіацій та хаосу. Від арабських геометричних візерунків до розкішних африканських зображень на тканинах пролягає цілий всесвіт візерунків для натхнення та пізнання.
[![Franz Sales Meyer - A handbook of ornament (1920)](geometricpatters.png)](https://archive.org/details/handbookoforname00meyeuoft/page/10/mode/2up)
Цією главою ми закінчуємо розділ про алгоритмічне малювання. У наступних розділах ми дізнаємося, як привносити у наші шейдери трохи ентропії та створювати генеративні дизайни.

@ -0,0 +1,92 @@
# Генеративний дизайн
Не дивно, що після стількох повторень наших карате-рухів і впорядкованості, автор змушений внести трохи хаосу.
## Випадковість
[![Ryoji Ikeda - тестовий патерн (2008) ](ryoji-ikeda.jpg)](http://www.ryojiikeda.com/project/testpattern/#testpattern_live_set)
Випадковість є максимальним проявом ентропії. Як ми можемо створити випадковість у, здавалося б, передбачуваному та суворому програмному середовищі?
Почнемо з аналізу наступної функції:
<div class="simpleFunction" data="y = fract(sin(x) * 1.0);"></div>
Значення [```sin()```](../glossary/?lan=ua&search=sin) коливаються між ```-1.0``` до ```1.0```. Вище ми витягуємо дробову частину цієї синусоїди, "обрізаючи" та підіймаючи діапазон її негативних значень до позитивних в межах від ```0.0``` до ```1.0```. Цей ефект можна використати, щоб отримати деякі псевдовипадкові значення, "розбивши" синусоїду на менші частини. Як? За допомогою помноження результату [```sin(x)```](../glossary/?lan=ua&search=sin) на більші числа. Поверніться до прикладу і почніть додавати нулі до цілої частини множника.
Коли дійдете до ```100000.0``` і рівняння виглядатиме як "```y = fract(sin(x) * 100000.0)```", то більше не зможете розрізнити синусоїду. Деталізація дробової частини спотворила плавний потік синусоїди, перетворивши її у псевдовипадковий хаос.
## Управління хаосом
Використання випадковості може бути важкою справою. Інколи вона буває занадто хаотичною, а часом недостатньо випадковою. Подивіться на наступний графік. В ньому ми використовуємо функцію ```rand()```, яка реалізована так само як ми описали вище.
Придивившись ближче, ви можете побачити гребні у [```sin()```](../glossary/?lan=ua&search=sin)-хвилі по x-вісі біля значень ```-1.5707``` та ```1.5707```. Б'юся об заклад, тепер ви розумієте чому — саме там відбувається максимум і мінімум синусоїди.
Якщо уважно розглянути цей розподіл псевдовипадкових значень, то ви також помітите, що навколо середини y-вісі спостерігається більша концентрація значень, аніж на її краях.
<div class="simpleFunction" data="y = rand(x);
//y = rand(x) * rand(x);
//y = sqrt(rand(x));
//y = pow(rand(x), 5.0);"></div>
Свого часу [Pixelero](https://pixelero.wordpress.com) опублікував [цікаву статтю про випадковий розподіл](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/). Я додав декілька його прикладів до попереднього графіка, щоб ви могли пограти з ними та побачити, як можна змінити розподіл значень. Розкоментуйте рядки та подивіться, що станеться.
Читаючи [статтю Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/), важливо мати на увазі, що наша функція ```rand()``` є детерміновано випадковою, інакше кажучи, псевдовипадковою. Це означає, що, наприклад, ```rand(1.)``` завжди повертатиме одне й те саме значення. [Pixelero](https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/) у своїй статті посилається на недетерміновану ActionScript-функцію ```Math.random()```, кожен виклик якої повертає різні значення.
## Двомірна випадковість
Тепер, коли ми краще розуміємо випадковість, настав час застосувати її у двомірному просторі, до осей ```x``` та ```y```. Для цього нам потрібен спосіб перетворити двовимірний вектор в одновимірне значення з рухомою крапкою. Існують різні способи зробити це, але функція [```dot()```](../glossary/?lan=ua&search=dot) особливо корисна в цьому випадку. Вона повертає єдине float-значення між ```0.0``` і ```1.0``` залежно від розташування двох векторів.
<div class="codeAndCanvas" data="2d-random.frag"></div>
Подивіться на рядки з 13 по 15 і зверніть увагу на те, як ми порівнюємо ```vec2 st``` з іншим двовимірним вектором (```vec2(12.9898, 78.233)```).
* Спробуйте змінити значення в рядках 14 і 15. Подивіться як змінюється зображення та поміркуйте, чого ми можемо з цього навчитися.
* Застосуйте цю функцію з отримання випадкових значень до взаємодії зі змінною для курсора миші (```u_mouse```) та часом (```u_time```), щоб краще зрозуміти, як вона працює.
## Використання хаосу
Випадкові значення у двовимірному зображенні дуже схожі на телевізійний шум, чи не так? Це жорсткий та занадто сирий матеріал для створення зображень. Навчімось же ним користуватися.
Першим кроком застосуємо до нього сітку. За допомогою функції [```floor()```](../glossary/?lan=ua&search=floor) ми згенеруємо таблицю з цілочисельною кількістю клітинок. Подивіться на наступний код, особливо на рядки 22 і 23:
<div class="codeAndCanvas" data="2d-random-mosaic.frag"></div>
Після збільшення простору у 10 разів (рядок 21), ми відокремлюємо цілі числа координат від дробової частини (рядок 22). Ми знайомі з цією операцією, тому що вже використовували її для поділу простору на менші комірки, що йдуть від ```0.0``` до ```1.0```. Отримавши ціле значення координати, ми виокремлюємо загальне значення кількості пікселів, необхідних для побудови одної комірки. Далі ми можемо використовувати це ціле число, щоб отримати випадкове значення для цієї області. Оскільки наша випадкова функція є детермінованою, то випадкове значення, що повертається, буде постійним для всіх пікселів у цій клітинці.
Розкомментуйте рядок 29, щоб побачити, що ми зберігаємо дробову частину координати (рядок 23), тому ми все ще можемо використовувати її як нормовану систему координат для кожної окремої клітинки.
Поєднання цілої та дробової частин координати дозволить змішувати варіацію та порядок.
Подивіться на наступну GLSL-версію відомого генератора лабіринтів [```10 PRINT CHR$(205.5 + RND(1)); : GOTO 10```](https://10print.org/).
<div class="codeAndCanvas" data="2d-random-truchet.frag"></div>
Тут я використовую випадкові значення комірок для малювання лінії в одному чи іншому напрямку за допомогою функції ```truchetPattern()``` із попереднього розділу (рядки 41-47).
Розкоментувавши блок рядків з 50 по 53, ви можете отримати інший цікавий патерн, або анімувати його, розкоментувавши рядки 35 і 36.
## Майстер випадковості
[Ryoji Ikeda](http://www.ryojiikeda.com/) — японський електронний композитор і візуальний художник, який опанував використання випадковості. Важко залишитись байдужим та не стати загіпнозованим його роботами. При використанні випадковості у своїх аудіо- та візуальних середовищах він не перетинає межу дратівливого хаосу та відзеркалює складність нашої технологічної культури.
<iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
Перегляньте роботу [Ikeda](http://www.ryojiikeda.com/) і спробуйте виконати такі вправи:
* Створіть ряди рухомих у протилежних напрямках комірок із випадковими значеннями. Показуйте лише клітинки найбільш яскравими значеннями. Зробіть так, щоб швидкість рядків з часом змінювалася:
<a href="../edit.php#10/ikeda-00.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-00.frag" width="520px" height="200px"></canvas></a>
* Так само зробіть кілька рядів, але кожен з різною швидкістю та напрямком. Зробіть, щоб кількість видимих клітинок залежали від положення вказівника миші:
<a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag" width="520px" height="200px"></canvas></a>
* Створіть інші цікаві ефекти:
<a href="../edit.php#10/ikeda-04.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-04.frag" width="520px" height="200px"></canvas></a>
Використовування випадковості для отримання естетичних результатів може бути проблематичним, особливо якщо ви хочете зробити симуляції, що виглядатимуть природно. Випадковість занадто хаотична й дуже мало речей виглядають **`рандомно`** у реальному житті. Якщо поглянути на патерн дощу або біржову діаграму, які є досить випадковими, то вони зовсім не схожі на ту модель випадковості, яку ми створили на початку цього розділу. В чому причина? Ну що ж, просто ці випадкові значення не мають ніякої кореляції між собою, а більшість природних шаблонів мають певну "пам'ять" про свій попередній стан.
У наступному розділі ми дізнаємося про шум, плавний і *природний* спосіб створення обчислювального хаосу.

@ -0,0 +1,220 @@
![NASA / WMAP science team](mcb.jpg)
## Шум
Час взяти перерву! Ми гралися з функціями випадкових значень, що нагадують телевізійний білий шум, наші голови все ще кружляють від думок про шейдери та втомились очі. Час піти на прогулянку!
Ми відчуваємо повітря на нашій шкірі, сонце на обличчі. Світ такий яскравий та насичений. Кольори, текстури, звуки. На прогулянці ми не можемо не помітити поверхню доріг, каменів, дерев та хмар.
![](texture-00.jpg)
![](texture-01.jpg)
![](texture-02.jpg)
![](texture-03.jpg)
![](texture-04.jpg)
![](texture-05.jpg)
![](texture-06.jpg)
Непередбачуваність цих текстур можна назвати "випадковою", але вони не схожі на той рандом, з якими ми грали раніше. "Реальний світ" — це дуже багате й складне місце! Як ми можемо апроксимувати цю варіативність за допомогою обчислень?
Це питання [Кен Перлін](https://mrl.nyu.edu/~perlin/) намагався вирішити на початку 1980-х років, коли йому доручили створити більш реалістичні текстури для фільму "Трон". У результаті він придумав елегантний алгоритм для генерації шуму за який потім отримав своєрідний *Оскар*. Нічого особливого)
![Disney - Tron (1982)](tron.jpg)
Нижче наведено не класичний алгоритм шуму Перліна, але це хороша відправна точка для розуміння того, як генерувати шум.
<div class="simpleFunction" data="
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i); //rand() is described in the previous chapter
// y = mix(rand(i), rand(i + 1.0), f);
// y = mix(rand(i), rand(i + 1.0), smoothstep(0., 1., f));
"></div>
У цих рядках ми робимо щось подібне до того, що робили в попередньому розділі. Ми ділимо безперервне число з рухомою крапкою (`x`) на цілу (`i`) і дробову (`f`) складові. Для отримання цілої частини "**`i`**" ми використовуємо [```floor()```](../glossary/lan=ua&?search=floor), а для дробової частини "**`f`**" — [```fract()```](../glossary/lan=ua&?search=fract). Потім ми застосовуємо ```rand()``` до цілої частини "**`x`**", що дає унікальне випадкове значення для кожного цілого числа.
Також ви бачите два закоментовані рядки. Перший інтерполює кожне випадкове значення лінійним чином.
```glsl
y = mix(rand(i), rand(i + 1.0), f);
```
Розкоментуйте цей рядок, щоб побачити його роботу. Ми використовуємо дробове значення **`f`**, отримане після [```fract()```](../glossary/lan=ua&?search=fract), щоб за допомогою [```mix()```](../glossary/lan=ua&?search=mix) зміксувати два випадкових значення.
Ми вже знаємо, що можемо використати краще рішення, ніж лінійна інтерполяція, чи не так?
Розкоментуйте наступний рядок, щоб застосувати інтерполяцію [```smoothstep()```](../glossary/lan=ua&?search=smoothstep).
```glsl
y = mix(rand(i), rand(i + 1.0), smoothstep(0., 1. ,f));
```
Зверніть увагу на те, як перехід між вершинами стає плавним. У деяких реалізаціях шуму програмісти віддають перевагу створенню власних кубічних кривих (наприклад, наступна формула) замість використання [```smoothstep()```](../glossary/lan=ua&?search=smoothstep).
```glsl
float u = f * f * (3.0 - 2.0 * f ); // варіант кубічної кривої
y = mix(rand(i), rand(i + 1.0), u); // використання її в інтерполяції
```
Ця *плавна випадковість* змінює правила гри для графічних інженерів і художників. Вона надає можливість генерувати зображення та геометрію, що виглядатимуть органічно. Алгоритм шуму Перліна реалізовано на різних мовах та для різних вимірів, що дозволяє створювати захопливі витвори мистецтва для різноманітних творчих цілей.
![Robert Hodgin - Written Images (2010)](robert_hodgin.jpg)
Тепер настала ваша черга:
* Створіть власну функцію типу "```float noise(float x)```".
* Використайте функцію шуму для анімації фігури, переміщуючи її, обертаючи або змінюючи масштаб.
* Використовуючи шум, створіть анімаційну композицію з кількох рухомих фігур.
* Сконструюйте за допомогою шуму "органічні" форми.
* Розвиньте отриману "істоту" у персонаж, задавши їй певний рух.
## Двомірний шум
![](02.png)
Тепер, коли ми знаємо, як створити одномірний шум (1D - one dimention), настав час перейти до 2D. У 2D, замість інтерполяції між двома точками лінії (```rand(x)``` та ```rand(x) + 1.0```), ми будемо інтерполювати між чотирма кутами квадратної площини (```rand(st)```, ```rand(st) + vec2(1., 0.)```, ```rand(st) + vec2(0., 1.)``` та ```rand(st) + vec2(1., 1.)```).
![](01.png)
Так само, якщо ми хочемо отримати тривимірний шум, то потрібно інтерполювати між вісьмома кутами куба. Ця техніка пов'язана з інтерполяцією випадкових значень, тому її називають **value noise (шумом значення)**.
![](04.jpg)
Як і з одномірним прикладом, ця інтерполяція не лінійна, а кубічна, яка плавно інтерполює будь-які точки всередині нашої квадратної сітки.
![](05.jpg)
Погляньте на наступну функцію шуму:
<div class="codeAndCanvas" data="2d-noise.frag"></div>
Ми починаємо з масштабування простору у 5 разів (рядок 45), щоб побачити інтерполяцію між квадратами сітки. Потім у функції шуму ми ділимо простір на комірки. Дробову частину координати ми зберігаємо для використання всередині клітини, а цілу частину — як координату самої клітини. Ми використовуємо цілочисельне значення позиції, щоб обчислити координати чотирьох кутів комірки та отримати випадкове значення для кожного з них (рядки 23-26). Нарешті, у рядку 35 ми інтерполюємо між 4 випадковими значеннями кутів, використовуючи дробові позиції, які ми зберегли раніше.
Тепер ваша черга. Спробуйте наступні вправи:
* Змініть множник у рядку 45. Спробуйте анімувати його.
* На якому рівні масштабування шум знову починає виглядати випадковим?
* На якому рівні масштабування шум непомітний?
* Спробуйте зробити функцію шуму залежною від координат курсору.
* Що, якщо розглядати градієнт шуму як поле відстаней? Зробіть з ним щось цікаве.
* Тепер, коли ви досягли певного контролю над порядком та хаосом, настав час використати ці знання. Створіть композицію з прямокутниками, кольорами й шумом, яка нагадувала б комплексність картини [Марка Ротко](http://en.wikipedia.org/wiki/Mark_Rothko).
![Mark Rothko - Three (1950)](rothko.jpg)
## Використання шуму в генеративному дизайні
Алгоритми шуму початково були розроблені, щоб надати цифровим текстурам певної природної якості. Одномірна та двомірна реалізації, які ми бачили до цього часу, були інтерполяцією між випадковими *значеннями*, тому вони називаються **шумом значень**, але існують й інші способи отримати шум...
[![Inigo Quilez - Value Noise](value-noise.png)](../edit.php#11/2d-vnoise.frag)
Як ви виявили в попередніх вправах, значення шуму має тенденцію виглядати "блоковим". Щоб зменшити цей блоковий ефект, у 1985 році [Кен Перлін](https://mrl.nyu.edu/~perlin/) розробив іншу реалізацію алгоритму під назвою **градієнтний шум**. Кен зрозумів як інтерполювати випадкові *градієнти* замість значень. Ці градієнти були результатом двомірної випадкової функції, яка повертає напрямки (у вигляді ```vec2```) замість окремих значень типу ```float```. Клацніть на наступне зображення, щоб побачити код та як він працює.
[![Inigo Quilez - Gradient Noise](gradient-noise.png)](../edit.php#11/2d-gnoise.frag)
Знайдіть хвилинку, щоб переглянути ці два приклади, за авторства [Inigo Quilez](http://www.iquilezles.org/), та зверніть увагу на відмінності між [шумом значень](https://www.shadertoy.com/view/lsf3WH) та [градієнтним шумом](https://www.shadertoy.com/view/XdXGW8).
Подібно до художника, який розуміється на роботі пігментних фарб, чим більше ми знаємо про імплементацію шуму, тим краще ми можемо їх використовувати. Наприклад, якщо за допомогою значень двомірного шуму обертати простір зображення з прямими лініями, то можна створити наступний ефект закручування, схожий на текстуру деревини. Клацніть на зображення, щоб побачити код:
[ ![Wood texture](wood-long.png) ](../edit.php#11/wood.frag)
```glsl
pos = rotate2d(noise(pos)) * pos; // обертання простору
pattern = lines(pos, .5); // малювання лінії
```
Ще один спосіб отримати цікаві візерунки за допомогою шуму це розглядати його як поле відстаней та застосувати деякі трюки, описані в [розділі про фігури](../07/?lan=ua).
[ ![Splatter texture](splatter-long.png) ](../edit.php#11/splatter.frag)
```glsl
color += smoothstep(.15, .2, noise(st * 10.)); // чорні бризки
color -= smoothstep(.35, .4, noise(st * 10.)); // дірки на бризках
```
Третій спосіб використання функції шуму це модуляція фігур. Для цього також потрібні певні трюки з [розділу про фігури](../07/?lan=ua).
<a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag" width="300px" height="300"></canvas></a>
Для вашої практики:
* Які ще генеративні патерни ви можете створити? Як щодо граніту? Мармуру? Магми? Води? Знайдіть три зображення текстур, які вас цікавлять та алгоритмічно реалізуйте їх, використовуючи шум.
* Використайте шум для модуляції форми.
* Як щодо використання шуму для руху? Поверніться до [розділу про матриці](../08/?lan=ua), використайте приклад з переміщенням хреста та застосуйте до його рухів трохи *випадковості* та *шуму*.
* Сгенеруйте зображення подібне до картини Джексона Поллака.
![Jackson Pollock - Number 14 gray (1948)](pollock.jpg)
## Покращений шум
Кен Перлін удосконалив свій оригінальний шум та зробив **симплексний шум**, що полягає в заміні кубічної кривої Ерміта ( _f(x) = 3x^2-2x^3_, яка є ідентичною до функції [```smoothstep()```](../glossary/?lan=ua&search=smoothstep)) на квінтичну інтерполяційну криву (криву п'ятого ступеня) ( _f(x) = 6x^5 - 15x^4 + 10x^3_ ). Це робить обидва кінці кривої більш "пласкими", тому кожна межа з'єднується з наступною плавніше. Іншими словами, ви отримуєте більш безперервний перехід між осередками. Ви можете побачити це, розкоментувавши другу формулу в наступному прикладі графіка (або перегляньте обидва рівняння [тут](https://www.desmos.com/calculator/2xvlk5xp8b)).
<div class="simpleFunction" data="
// Кубічна крива Ерміта. Те саме, що smoothStep()
y = x * x * (3.0 - 2.0 * x);
// Крива п'ятого ступеня
// y = x * x * x * (x * (x * 6. - 15.) + 10.);
"></div>
Зверніть увагу на те, як змінюються кінці кривої. Ви можете прочитати більше про це у [поясненнях самого Кена](http://mrl.nyu.edu/~perlin/paper445.pdf).
## Симплексний шум
Кен Перлін не задовольнився успіхом свого алгоритму. Він гадав, що зможе досягти для нього більшої продуктивності. На Siggraph 2001 він представив "симплексний шум", у якому досяг наступних покращень у порівнянні з попередньою версією:
* Менша обчислювальна складність та менша кількість множень.
* Шум масштабується до вищих розмірностей з меншими обчислювальними витратами.
* Шум без направлених артефактів.
* Шум має чітко визначений безперервний градієнт, який обчислюється досить дешево.
* Алгоритм достатньо легко реалізувати для апаратного забезпечення.
Я знаю про що ви думаєте... "Хто цей чоловік?" Так, його робота фантастична! Але серйозно, як він покращив алгоритм? Розберімось. Для двох вимірів він інтерполював 4 точки (кути квадрата), для трьох [(див. реалізацію)](../edit.php#11/3d-noise.frag) та чотирьох вимірів нам потрібно інтерполювати вже 8 і 16 точок відповідно. Правильно? Іншими словами, для N вимірів вам потрібно плавно інтерполювати 2 у степені N точок (2^N). Але Кен розумно помітив, що хоча очевидним вибором форми, що заповнює простір, є квадрат, найпростішою фігурою у двомірному просторі є рівносторонній трикутник. Тому він почав із заміни квадратної сітки (яку ми щойно навчилися використовувати) на симплексну сітку рівносторонніх трикутників.
![](simplex-grid-00.png)
Симплексна форма для N-розмірності це багатокутник з N + 1 кутами. Іншими словами, у двомірному просторі можна буде обчислювати на один кут менше, у тримірному на 4 кути менше, а у чотиримірному на 11 кутів менше! Це величезне покращення!
У двох вимірах інтерполяція відбувається подібно до звичайного шуму, шляхом інтерполяції значень кутів ділянки. Однак у випадку з симплексною сіткою, нам потрібно інтерполювати лише 3 кути.
![](simplex-grid-01.png)
Як створюється симплексна сітка? Іншим блискучим і елегантним ходом є те, що симплексну сітку можна отримати, розділивши комірки правильної сітки з 4 кутами на два рівнобедрених трикутники, а потім трохи скосити їх, доки кожен трикутник не стане рівностороннім.
![](simplex-grid-02.png)
Далі, як описав [Stefan Gustavson у статті "Simplex noise demystified"](https://web.archive.org/web/20230310204121/https://weber.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf): _"...розглянувши цілі частини трансформованої координати (x, y) для точки, яку ми хочемо визначити, можна швидко визначити, яка саме клітинка з двох симплексів її вміщує. Порівнявши абсолютні величини x і y, ми можемо визначити, чи точка знаходиться у верхньому чи нижньому симплексі та обійти через три кутові точки."_
У наступному коді ви можете розкоментувати рядок 44, щоб побачити перекіс сітки, а потім розкоментувати рядок 47, щоб побачити побудову симплексної сітки. Зверніть увагу, як у рядку 22 ми ділимо скошений квадрат на два рівносторонні трикутники, просто визначаючи, чи ```x > y``` ("нижній" трикутник) чи ```y > x``` ("верхній" трикутник).
<div class="codeAndCanvas" data="simplex-grid.frag"></div>
Результатом усіх цих удосконалень є алгоритмічний шедевр, відомий як **Симплексний шум**. Нижче наведено GLSL-реалізацію цього алгоритму, розроблену за участі Ian McEwan та Stefan Gustavson (представлена у роботі ["Efficient computational noise in GLSL"](https://web.archive.org/web/20190203222422/http://weber.itn.liu.se/~stegu/jgt2012/article.pdf)), яка занадто складна для простих освітніх цілей, але ви із задоволенням побачите, що вона менш загадкова, ніж можна було очікувати, а код короткий та швидкий.
[ ![Ian McEwan of Ashima Arts - Simplex Noise](simplex-noise.png) ](../edit.php#11/2d-snoise-clear.frag)
Що ж... досить технічних нюансів, настав ваш час для використання цього ресурсу у якості нового засобу виразності:
* Поміркуйте, як виглядає кожна реалізація шуму. Уявіть їх як необроблену сировину, як мармурову скелю для скульптора. Що ви можете сказати про відчуття до кожної з них? Примружте очі для активізації уяви, наче видивляєтеся якісь фігури в хмарах. Що ви бачите? Про що це нагадує? Що підказує вам ваша уява, що можна зробити з кожною реалізацією шуму? Слідуйте за своїм чуттями та спробуйте втілити ідеї в коді.
* Створіть шейдер, який створює ілюзію потоку. Подібно до лавової лампи, крапель чорнила, води тощо.
<a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag" width="520px" height="200px"></canvas></a>
* Використовуючи симплексний шум, додайте трохи текстурності до виконаних раніше робіт.
<a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag" width="520px" height="520px"></canvas></a>
У цьому розділі ми навчилися певного контролю над хаосом. Це була нелегка робота! Для того, щоб стати майстром з використання шуму, потрібні час та зусилля.
У наступних розділах ми оглянемо деякі добре відомі методи для вдосконалення ваших навичок та отримаємо більше користі від шуму для розробки якісної генеративної графіки за допомогою шейдерів. А поки що трохи відпочиньте та насолодіться часом на свіжому повітрі, споглядаючи природу та її складні візерунки. Ваша здатність до спостереження потребує такої ж, або навіть більшої, уваги, ніж ваші творчі навички. Вийдіть на вулицю та насолодіться рештою дня!
<p style="text-align:center; font-style: italic;">"Поговори з деревом, подружися з ним." Bob Ross
</p>

@ -202,7 +202,7 @@ All these improvements result in an algorithmic masterpiece known as **Simplex N
Well... enough technicalities, it's time for you to use this resource in your own expressive way:
* Contemplate how each noise implementation looks. Imagine them as a raw material, like a marble rock for a sculptor. What can you say about about the "feeling" that each one has? Squinch your eyes to trigger your imagination, like when you want to find shapes in a cloud. What do you see? What are you reminded of? What do you imagine each noise implementation could be made into? Following your guts and try to make it happen in code.
* Contemplate how each noise implementation looks. Imagine them as a raw material, like a marble rock for a sculptor. What can you say about the "feeling" that each one has? Squinch your eyes to trigger your imagination, like when you want to find shapes in a cloud. What do you see? What are you reminded of? What do you imagine each noise implementation could be made into? Following your guts and try to make it happen in code.
* Make a shader that projects the illusion of flow. Like a lava lamp, ink drops, water, etc.

@ -0,0 +1,191 @@
![](dragonfly.jpg)
## Клітинний шум
У 1996 році, через шістнадцять років після оригінального шуму Перліна та за п’ять років до його симплексного шуму, Steven Worley написав статтю під назвою [“Базова функція клітинної текстури”](http://www.rhythmiccanvas.com/research/papers/worley.pdf). У ній він описує техніку процедурного текстурування, яка зараз широко використовується графічною спільнотою.
Щоб зрозуміти принципи, що лежать в її основі, нам потрібно почати думати в термінах **ітерацій**. Напевно, ви розумієте, що це означає використання циклів ```for```. У циклах ```for``` в GLSL є лише одна заковика: число, яке використовується у перевірці лічильника, має бути константою ([```const```](../glossary/?lan=ua&search=const)). Отже, ніяких динамічних циклів — кількість ітерацій має бути фіксованою.
Давайте розглянемо приклад.
### Точки у полі відстаней
Клітинний шум базується на полях відстаней, що розташовані до найближчої точки з певного набору. Скажімо, ми хочемо створити поле відстаней з 4 точок. Що нам потрібно зробити? **Для кожного пікселя ми хочемо обчислити відстань до найближчої точки**. Це означає, що нам потрібно виконати ітерацію по всім точкам, обчислити до них відстані від поточного пікселя та зберегти значення до найближчої.
```glsl
float min_dist = 100.; // змінна для збереження відстані до найближчої точки
min_dist = min(min_dist, distance(st, point_a));
min_dist = min(min_dist, distance(st, point_b));
min_dist = min(min_dist, distance(st, point_c));
min_dist = min(min_dist, distance(st, point_d));
```
![](cell-00.png)
Не дуже елегантно, але робить свою справу. Тепер реалізуємо цей підхід за допомогою масиву та циклу ```for```:
```glsl
float m_dist = 100.; // мінімальна відстань
for (int i = 0; i < TOTAL_POINTS; i++) {
float dist = distance(st, points[i]);
m_dist = min(m_dist, dist);
}
```
Зверніть увагу, як ми використовуємо цикл ```for```, щоб перебирати масив точок і відстежувати мінімальну відстань за допомогою функції [```min()```](../glossary/?lan=ua&search=min). Ось коротка робоча реалізація цієї ідеї:
<div class="codeAndCanvas" data="cellnoise-00.frag"></div>
У наведеному вище коді одна з точок прив'язана до положення курсору. Пограйте з прикладом, щоб отримати краще інтуїтивне уявлення щодо поведінки програми. Потім спробуйте наступне:
- Як анімувати решту точок?
- Прочитавши [розділ про фігури](../07/?lan=ua), уявіть цікаві способи використання з цим полем відстаней!
- Як додати більше точок до цього поля відстаней? Що можна зробити для динамічного додавання або видалення точок?
### Замощення та ітерація
Ви, мабуть, помітили, що цикли ```for``` та *масиви* не дуже дружать з GLSL. Як ми вже говорили раніше, місцеві цикли не здатні на динамічну кількість ітерацій. Крім того, велика кількість ітерацій значно знижує продуктивність вашого шейдера. Це означає, що ми не можемо використовувати цей прямолінійний підхід для великої кількості точок. Нам потрібно знайти іншу стратегію, яка буде використовувати переваги архітектури GPU з її паралельними обчисленнями.
![](cell-01.png)
Один з підходів до розв'язання цієї проблеми — це розділення простору на плитки. Кожному пікселю не обов'язково перевіряти відстань до кожної окремої точки, чи не так? Враховуючи той факт, що кожен піксель працює у власному потоці, ми можемо розділити простір на комірки, кожна з яких матиме одну унікальну точку для спостереження. Крім того, щоб уникнути аберацій на межах між комірками, нам потрібно перевірити відстань до точок лише у сусідніх клітинах. Це і є головною чудовою ідеєю у [статті Steven Worley](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Зрештою, кожен піксель має перевірити лише дев’ять позицій: точку своєї клітини та точки у 8 клітинах навколо неї. Ми вже поділяли простір на комірки в розділах про: [патерни](../09/?lan=ua), [випадковість](../10/?lan=ua) та [шум](../11/?lan=ua), тож, сподіваюся, ви вже знайомі з цією технікою.
```glsl
// Масштаб
st *= 3.;
// Розділ простору на плитки
vec2 i_st = floor(st);
vec2 f_st = fract(st);
```
Отже, який план? Ми будемо використовувати координати клітини-плитки, збережені в цілочисельній координаті ```i_st```, щоб побудувати випадкову позицію точки. Функція ```random2f```, яку ми будемо використовувати, отримує на вхід ```vec2``` та повертає нам також ```vec2``` з випадковою для даної клітини координатою. Таким чином, для кожної плитки ми матимемо одну особливу точку у випадковому місці цієї плитки.
```glsl
vec2 point = random2(i_st);
```
Кожен піксель усередині плитки, з координатою збереженою у змінній ```f_st```, перевірятиме свою відстань до випадкової точки, про яку ми говорили раніше.
```glsl
vec2 diff = point - f_st;
float dist = length(diff);
```
Результат буде виглядати так:
<a href="../edit.php#12/cellnoise-01.frag"><img src="cellnoise.png" width="520px" height="200px"></img></a>
Нам все ще потрібно перевірити відстані до точок у навколишніх клітинах, а не лише на поточній. Для цього нам потрібно **ітерувати** сусідні клітини. Не всі клітини, а лише ті, що знаходяться безпосередньо навколо поточної. Тобто від ```-1``` клітини ліворуч, до ```+1``` клітини праворуч по осі ```x``` та від ```-1``` клітини знизу до ```+1``` клітини зверху по осі ```y```. Цю ділянку розміром 3x3 із 9 клітин можна обійти за допомогою подвійного циклу ```for```:
```glsl
for (int y= -1; y <= 1; y++) {
for (int x= -1; x <= 1; x++) {
// Сусідня позиція клітини у сітці
vec2 neighbor = vec2(float(x), float(y));
...
}
}
```
![](cell-02.png)
Тепер у вкладеному циклі ```for``` ми можемо обчислити положення наших випадкових точок у кожній сусідній клітині, додаючи зміщення сусідньої клітини до координат поточної.
```glsl
...
// Положення випадкової точки у сусідній плитці
vec2 point = random2(i_st + neighbor);
...
```
Решта зводиться до обчислення відстаней до цієї точки та збереження найближчої у змінну ```m_dist```.
```glsl
...
vec2 diff = neighbor + point - f_st;
// Відстань до точки
float dist = length(diff);
// Збереження ближчої дистанції
m_dist = min(m_dist, dist);
...
```
Наведений вище код створено на основі [статті Inigo's Quilez](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm), де він писав:
*"... варто зазначити, що у наведеному вище коді є гарний трюк. Більшість реалізацій страждають від проблем із точністю, оскільки вони генерують свої випадкові точки в просторі "домену" (наприклад, у "глобальному" просторі чи просторі "об'єкта"), який може бути як завгодно далеко від початку координат. Проблему можна вирішити, перемістивши весь код до типів даних з вищою точністю, або проявивши трохи кмітливості. Моя реалізація генерує точки не в просторі "домену", а в просторі "комірки": як тільки із фрагментного пікселя отримано цілу та дробову частини, а значить визначено клітину, в якій ми працюємо, нас цікавитиме лише те, що відбувається навколо цієї клітини, а не весь простір. Тобто ми можемо відкинути цілочисельну частину значення наших координат та зберегти кілька біт точності. Насправді у звичайній реалізації діаграми Вороного, цілі частини координат точки просто скасовуються, коли випадкові точки клітини віднімаються від поточної шейдерної точки. У реалізації вище ми не робимо цього, оскільки переносимо всі обчислення в простір "комірки". Цей підхід дозволяє накласти діаграму Вороного хоч на цілу планету — достатньо просто подвоїти точність для вхідних даних, виконати для них обчислення з floor() і fract(), а решту обчислень проводити вже зі звичайною точністю для float, не витрачаючи обчислювальні ресурси на зміну всієї реалізації до подвійної точності. Звісно, той самий трюк можна застосувати й до підходів з шумом Перліна, але я ніколи не бачив подібну реалізацію чи опис подібного алгоритму)."*
Підсумовуючи: розбиваємо простір на клітини. Кожен піксель обчислює відстань до точки у власній клітині та навколишніх 8 клітинах, зберігаючи найближчу відстань. В результаті маємо поле відстаней, що виглядає як на прикладі нижче:
<div class="codeAndCanvas" data="cellnoise-02.frag"></div>
Дослідіть цей приклад глибше:
- Масштабуйте простір різними значеннями.
- Вигадайте інші способи анімації точок.
- Що потрібно зробити для обчислення додаткової точки у положенні курсору?
- Які інші способи побудови цього поля відстаней ви можете собі уявити, окрім ```m_dist = min(m_dist, dist);```?
- Які цікаві візерунки можна зробити за допомогою цього поля відстаней?
Цей алгоритм також можна інтерпретувати з точки зору точок, а не пікселів. У цьому випадку його можна описати таким чином: кожна точка росте, доки не натрапить на область росту іншої точки. Ця поведінка відображає деякі правила росту в природі. Живі організми формуються через цю напругу між внутрішньою силою зростання й розширення та обмеженнями зовнішніх сил. Класичний алгоритм, який імітує таку поведінку, названий на честь [Георгія Вороного](https://en.wikipedia.org/wiki/Georgy_Voronoy).
![](monokot_root.jpg)
### Алгоритм Вороного
Побудувати діаграми Вороного з клітинного шуму не так складно, як може здатися. Нам просто потрібно *зберегти* додаткову інформацію про найближчу точку до пікселя. Для цього ми використаємо змінну ```m_point``` типу ```vec2```. Зберігаючи вектор напрямку до найближчої точки замість відстані, ми "отримаємо" "унікальний" ідентифікатор цієї точки.
```glsl
...
if ( dist < m_dist ) {
m_dist = dist;
m_point = point;
}
...
```
Зауважте, що в наступному коді, для обчислення найближчої відстані ми використовуємо звичайний оператор ```if``` замість функції ```min```. Чому? Тому що, при знаходженні нової найближчої точки, ми хочемо зробити додаткову дію і зберегти її координати (рядки 32-37).
<div class="codeAndCanvas" data="vorono-00.frag"></div>
Зверніть увагу, як колір рухомої комірки, прив'язаної до положення курсору, змінює колір відповідно до його положення. Це тому, що колір визначається залежно від позиції найближчої точки.
Як і у попередніх прикладах, настав час збільшити масштаби, скориставшись підходом зі [статті за авторства Steven Worley](http://www.rhythmiccanvas.com/research/papers/worley.pdf). Спробуйте реалізувати його самостійно. Ви можете скористатися допомогою наступного прикладу, клацнувши на нього. Зауважте, що оригінальний підхід від Steven Worley використовує змінну кількість точок для кожної клітини, більш ніж одну у більшості випадків. У його програмній реалізації на мові C це використовується для прискорення циклу завдяки раннім виходам із нього. Цикли GLSL не дозволяють змінювати кількість ітерацій, тому вам, ймовірно, доведеться використовувати лише одну точку на клітину.
<a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag" width="520px" height="200px"></canvas></a>
Розібравшись із цим алгоритмом, подумайте про цікаві та креативні способи його використання.
![Extended Voronoi - Leo Solaas (2011)](solas.png)
![Cloud Cities - Tomás Saraceno (2011)](saraceno.jpg)
![Accretion Disc Series - Clint Fulkerson](accretion.jpg)
![Vonoroi Puzzle - Reza Ali (2015)](reza.png)
### Поліпшення діаграми Вороного
У 2011 році [Stefan Gustavson оптимізував алгоритм Steven Worley для GPU](https://web.archive.org/web/20220530233245/https://weber.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf), обходячи лише матрицю 2x2 замість 3x3. Це значно зменшує обсяг роботи, але може створювати артефакти у вигляді розривів на краях між клітинами. Подивіться на наступні приклади:
<div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div>
Пізніше у 2012 році [Inigo Quilez написав статтю про створення точних меж для комірок Вороного](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm).
<a href="../edit.php#12/2d-voronoi.frag"><img src="2d-voronoi.gif" width="520px" height="200px"></img></a>
На цьому експерименти Inigo з Вороним не закінчилися. У 2014 році він написав чудову статтю про функцію [voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm), яка дозволяє поступово змішувати звичайний шум з комірками Вороного. Ось вирізка з його слів:
*"Попри схожість, сітка в обох патернах використовується по різному. Шум інтерполює/усереднює випадкові значення (як у шумі значення) або градієнти (як у градієнтному шумі), тоді як алгоритм Вороного обчислює відстань до найближчої опорної точки. Плавна білінійна інтерполяція та обчислення мінімума — дві дуже різні операції чи... ні? Чи можна їх об’єднати у більш загальну метрику? Якщо так, то шум та патерни Вороного можна розглядати як окремі випадки більш загального генератора патернів на основі регулярної сітки?»*
<a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag" width="520px" height="200px"></canvas></a>
Настав час придивитися до навколишніх речей, надихнутися природою, знайти власну ідею та спробувати сили у цій техніці!
![Deyrolle glass film - 1831](DeyrolleFilm.png)
<div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>

@ -0,0 +1,111 @@
![Due East over Shadequarter Mountain - Matthew Rangel (2005) ](rangel.jpg)
## Фрактальний броунівський рух
Для різних людей слово "шум" означає різні речі. Музиканти будуть думати про безладні звуки, інженери зв'язку — про перешкоди, а астрофізики — про космічний мікрохвильовий фон. Ці концепції повертають нас до фізичної природи випадковості в навколишньому світі. Однак почнімо з чогось більш фундаментального та простішого: хвиль та їхніх характеристик. Хвиля — це коливання певної властивості з плином часу. Аудіохвилі — це коливання тиску повітря, електромагнітні хвилі — це коливання електричного та магнітного полів. Двома важливими характеристиками хвилі є її амплітуда та частота. Рівняння для простої лінійної (одномірної) хвилі виглядає так:
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = amplitude * sin(x * frequency);
"></div>
* Спробуйте змінити значення частоти та амплітуди, щоб зрозуміти їх поведінку.
* Використовуючи формотворчі функції, змінюйте амплітуду залежно від часу.
* Використовуючи формотворчі функції, змінюйте частоту залежно від часу.
Виконуючи останні дві вправи, ви "модулювали" синусоїду, створивши AM (амплітудно-модульовані) та FM (frequency modulated - частотно-модульовані) хвилі. Мої вітання!
Ще однією цікавою властивістю хвиль є здатність до їх поєднання, що формально називається суперпозицією. Закоментуйте, розкоментуйте та позмінюйте наступні рядки. Зверніть увагу на зміни загального вигляду, коли ми додаємо разом хвилі з різними амплітудами та частотами.
<div class="simpleFunction" data="
float amplitude = 1.;
float frequency = 1.;
y = sin(x * frequency);
float t = 0.01 * (-u_time * 130.0);
y += sin(x * frequency * 2.1 + t) * 4.5;
y += sin(x * frequency * 1.72 + t * 1.121) * 4.0;
y += sin(x * frequency * 2.221 + t * 0.437) * 5.0;
y += sin(x * frequency * 3.1122 + t * 4.269) * 2.5;
y *= amplitude * 0.06;
"></div>
* Поекспериментуйте, змінюючи частоту й амплітуду додаткових хвиль.
* Чи можна зробити дві хвилі, що нейтралізуватимуть одна одну? Як це буде виглядати?
* Чи можна скласти хвилі так, щоб вони посилювали одна одну?
У музиці кожна нота асоціюється з певною частотою. Частоти для цих нот відповідають певному порядку, який ми називаємо гамою. Подвоєння або зменшення частоти вдвоє відповідає зміні ноти на одну октаву.
Тепер, замість синусоїди, використаємо шум Перліна! Шум Перліна у своїй основній формі виглядає та відчувається, як синусоїда. Його амплітуда та частота дещо мінливі, але амплітуда залишається досить однорідною, а частота обмежена досить вузьким діапазоном навколо центральної частоти. В цілому шум не такий регулярний, як синусоїда, та з його допомогою легше створити видимість випадковості, об'єднавши кілька масштабованих версій. Вигляд суми синусоїд також можна зробити більш випадковим, але для цього потрібно багато різних хвиль, щоб приховати їх періодичну та регулярну природу.
Додаючи різні ітерації шуму (*октави*), у яких ми послідовно збільшуємо частоту на регулярну величину (*лакунарність*) та зменшуємо амплітуду (*посилення*) **шуму**, можна отримати деталізованіший шум та більше дрібних деталей. Ця техніка називається "фрактальний броунівський рух" (*fBM*) або просто "фрактальний шум". У найпростішому вигляді її можна створити за допомогою наступного коду:
<div class="simpleFunction" data="// Властивості
const int octaves = 1; // октави
float lacunarity = 2.0; // лакунарність
float gain = 0.5; // посилення
//
// Початкові значення
float amplitude = 0.5;
float frequency = 1.;
//
// Цикл по октавам
for (int i = 0; i < octaves; i++) {
&#9;y += amplitude * noise(frequency * x);
&#9;frequency *= lacunarity;
&#9;amplitude *= gain;
}"></div>
* Поступово збільшуйте кількість октав від 1 до 2, 4, 8 і 10. Спостерігайте за результатом.
* Коли матимете понад чотири октави, спробуйте змінити значення лакунарності.
* Також, коли октав понад 4, змініть значення для посилення (gain) та подивіться, що станеться.
Зверніть увагу, що з кожною додатковою октавою крива стає деталізованішою. Також зверніть увагу на прояви самоподібності, зі збільшенням кількості октав. Якщо збільшити масштаб кривої, менша її частина виглядатиме приблизно так само, як і вся крива, а кожен окремий відрізок виглядає більш-менш схоже на будь-який інший. Це важлива властивість математичних фракталів, яку ми моделюємо у нашому циклі. Ми не створюємо *справжній* фрактал, оскільки зупиняємо модуляцію після кількох ітерацій, але теоретично можна отримати справжній математичний фрактал, якщо дозволити циклу тривати вічно та додавати нескінченну кількість компонентів шуму. У комп'ютерній графіці ми завжди маємо обмеження щодо найдрібніших деталей, які можемо розпізнати. Наприклад, якщо об'єкти стають меншими за піксель, немає потреби робити нескінченні обрахунки, щоб створити видимість фракталу. Іноді може знадобитися багато доданків, але ніколи не знадобиться їх нескінченна кількість.
Наступний код показує реалізацію двомірного fBm, схожого на фрактальний візерунок:
<div class="codeAndCanvas" data="2d-fbm.frag"></div>
* Знизьте кількість октав, змінивши значення в рядку 37
* Змініть значення лакунарності у рядку 47
* Дослідіть результати, змінюючи значення підсилення в рядку 48
Ця техніка зазвичай використовується для побудови процедурних ландшафтів. Самоподібність fBm ідеально підходить для створення гір, тому що процеси ерозії, які створюють реальні гори, також надають їм вигляд самоподібності в широкому діапазоні масштабів. Якщо це вас зацікавило, вам варто прочитати [цю чудову статтю Inigo Quiles про вдосконалений шум](http://www.iquilezles.org/www/articles/morenoise/morenoise.htm).
![Blackout - Dan Holdsworth (2010)](holdsworth.jpg)
Використовуючи більш-менш таку саму техніку, можна також отримати й інші ефекти, такі як **турбулентність**. По суті, це fBm, але побудований з абсолютних значень шуму, що створює різкі впадини.
```glsl
for (int i = 0; i < OCTAVES; i++) {
value += amplitude * abs(snoise(st));
st *= 2.;
amplitude *= .5;
}
```
<a href="../edit.php#13/turbulence.frag"><img src="turbulence-long.png" width="520px" height="200px"></img></a>
Іншим представником цього сімейства алгоритмів є **хребти**, де гострі долини перевернуті догори дном, щоб натомість вийшли гострі гребні:
```glsl
n = abs(n); // створюємо складки
n = offset - n; // інвертуємо складки догори дном
n = n * n; // загострюємо складки ще більше
```
<a href="../edit.php#13/ridge.frag"><img src="ridge-long.png" width="520px" height="200px"></img></a>
Ще один варіант з корисними варіаціями можна здобути за допомогою перемноження компонентів шуму замість їх додавання. Також цікаво масштабувати наступні шумові функції на основі чогось, що залежить від попередніх сум складових. Коли ми робимо подібні речі, то віддаляємося від суворого визначення фракталу та переходимо у відносно невідому область "мультифракталів". Мультифрактали поки що не мають чіткого математичного визначення, але це не робить їх менш корисними для графіки. Насправді мультифрактальне моделювання дуже поширене в сучасному комерційному програмному забезпеченні для генерації рельєфу. Детальніше про це можна прочитати у 16 розділі 3-го видання книги "Текстурування та моделювання: процедурний підхід" ("Texturing and Modeling: a Procedural Approach"), автор Kenton Musgrave. На жаль, цю книгу перестали друкувати, але її все ще можна знайти в бібліотеках та вторинному ринку. Майте на увазі, що в інтернеті продається PDF-версія 1-го видання, яку не варто купувати, оскільки воно не містить жодного матеріалу про моделювання ландшафтів.
### Викривлення домену (просторової області)
[Inigo Quiles написав ще одну захопливу статтю](http://www.iquilezles.org/www/articles/warp/warp.htm) про використання fBm для деформації простору fBm. Вибух мозку, правда? Це як сон уві сні у фільмі "Початок" (Inception).
![f(p) = fbm(p + fbm(p + fbm(p))) - Inigo Quiles (2002)](quiles.jpg)
Менш екстремальний приклад цієї техніки показано у наступному коді, де викривлення використовується для отримання текстури, схожої на хмари. Зверніть увагу, що властивість самоподібності все ще присутня у результаті:
<div class="codeAndCanvas" data="clouds.frag"></div>
Викривлення координат текстури за допомогою шуму може бути дуже корисним, дуже веселим та диявольськи складним для освоєння. Це потужний інструмент, але щоб добре його використовувати, потрібен певний досвід. Корисним підходом для подібних маніпуляцій є зміщення координат за допомогою похідної (градієнта) шуму. На цій ідеї базується відома стаття під назвою ["Шум потоку"](http://evasion.imag.fr/Publications/2001/PN01/), автори Ken Perlin та Fabrice Neyret. Деякі сучасні реалізації шуму Перліна включають обчислення як функції, так і її аналітичного градієнта.

@ -0,0 +1,16 @@
## Фрактали
Coming soon ...
А поки що можете подивитися приклади з фракталами на платформі [shadertoy.com](https://www.shadertoy.com/results?query=fractals):
[Basic Fractal](https://www.shadertoy.com/view/Mss3Wf)
[Mandelbrot - distance](https://www.shadertoy.com/view/lsX3W4)
[Mandelbrot - smooth](https://www.shadertoy.com/view/4df3Rn)
[Mandelbrot: Power Transitioning](https://www.shadertoy.com/view/lls3D7)
[IFS - brute force](https://www.shadertoy.com/view/lss3zs)
[Julia - Distance 1](https://www.shadertoy.com/view/Mss3R8)
[Julia - Distance 3](https://www.shadertoy.com/view/4dXGDX)
[Julia - Traps 2](https://www.shadertoy.com/view/4dfGRn)
[Fractal Wheel 2.0](https://www.shadertoy.com/view/llfGD2)
[Koch Snowflake again](https://www.shadertoy.com/view/Mlf3RX)

@ -1,3 +1,16 @@
## Fractals
Comming soon ...
Coming soon ...
In the meantime, you can check out some examples with fractals on the [shadertoy.com](https://www.shadertoy.com/results?query=fractals) platform:
[Basic Fractal](https://www.shadertoy.com/view/Mss3Wf)
[Mandelbrot - distance](https://www.shadertoy.com/view/lsX3W4)
[Mandelbrot - smooth](https://www.shadertoy.com/view/4df3Rn)
[Mandelbrot: Power Transitioning](https://www.shadertoy.com/view/lls3D7)
[IFS - brute force](https://www.shadertoy.com/view/lss3zs)
[Julia - Distance 1](https://www.shadertoy.com/view/Mss3R8)
[Julia - Distance 3](https://www.shadertoy.com/view/4dXGDX)
[Julia - Traps 2](https://www.shadertoy.com/view/4dfGRn)
[Fractal Wheel 2.0](https://www.shadertoy.com/view/llfGD2)
[Koch Snowflake again](https://www.shadertoy.com/view/Mlf3RX)

@ -0,0 +1,81 @@
# Обробка зображень
## Текстури
![](01.jpg)
Графічні карти (GPU) мають для зображень спеціальні типи пам’яті. Зазвичай CPU зберігають зображення у вигляді масиву байтів, а GPU зберігають зображення як ```sampler2D```, що більше схоже на таблицю (або матрицю) векторів типу float. Що ще цікавіше, значення цієї *таблиці* векторів безперервні. Це означає, що значення між пікселями інтерполюються на низькому рівні.
Щоб скористатися цим функціоналом, нам спочатку потрібно *завантажити* зображення з CPU на GPU, а потім передати ```id``` текстури до відповідної [```uniform```](../05/?lan=ua)-змінної. Початкове завантаження зображення відбувається поза шейдером.
Після того, як текстуру завантажено та прив'язано до змінної через ```uniform sampler2D```, ви можете отримати конкретне значення кольору у певних координатах типу [```vec2```](../glossary/?lan=ua&search=vec2) за допомогою функції [```texture2D()```](../glossary/?lan=ua&search=texture2D) яка поверне колір типу [```vec4```](../glossary/?lan=ua&search=vec4).
```glsl
vec4 texture2D(sampler2D texture, vec2 coordinates)
```
Перегляньте наступний код, у якому ми завантажуємо зображення [Хвилі Хокусая (1830)](https://en.wikipedia.org/wiki/The_Great_Wave_off_Kanagawa) як ```uniform sampler2D u_tex0```, отримуємо та відображаємо кожен її піксель всередині полотна:
<div class="codeAndCanvas" data="texture.frag" data-textures="hokusai.jpg"></div>
Якщо ви звернете увагу, то помітите, що координати для текстури нормалізовані! Який сюрприз, правда? Координати текстур консистентні з нормалізованими координатами простору, що також знаходяться в діапазоні від 0.0 до 1.0.
Тепер, коли ви побачили, як правильно завантажувати текстуру, настав час поекспериментувати та дізнатися, що можна з нею зробити:
* Змініть масштаб текстури вдвічі.
* Поверніть текстуру на 90 градусів.
* Прив'яжіть координати до позиції курсору, щоб переміщати зображення.
Чому ви повинні бути в захваті від текстур? По-перше, забудьте про сумні 255 значень для кожного каналу. Як тільки ваше зображення перетворено на ```uniform sampler2D```, ви працюватимете у діапазоні значень від 0.0 до 1.0. Ось чому шейдери можуть створювати дійсно красиві ефекти постобробки.
По-друге, за допомогою координат [```vec2```](../glossary/?lan=ua&search=vec2) ви можете отримати значення кольору текстури навіть між пікселями. Як ми вже говорили раніше, значення текстури є континуумом. Це означає, що ви можете отримувати значення вашого зображення по всій його поверхні. Ці значення плавно змінюватимуться від пікселя до пікселя без стрибків!
Нарешті, ви можете налаштувати шейдер для повторення вашого зображення вздовж полотна. Наприклад, при використанні значень, що менше або більше від нормалізованих 0.0 та 1.0 ви можете "загорнути" їх у цей самий діапазон та знову отримати кольори в межах зображення.
Усі ці можливості роблять ваші зображення схожими на нескінченну тканину спандекс. Ви можете розтягувати та звужувати свою текстуру, не зважаючи на сітку байтів, з яких вона складалася початково. Щоб відчути це, подивіться на наступний код, де ми деформуємо текстуру за допомогою [функції шуму, з попередніх розділів](../11/?lan=ua).
<div class="codeAndCanvas" data="texture-noise.frag" data-textures="hokusai.jpg"></div>
## Роздільна здатність текстури
Наведені вище приклади добре працюють з квадратними зображеннями, де обидві сторони рівні та збігаються з нашим квадратним полотном. Але для неквадратних зображень все може бути трохи складніше. Століття художнього мистецтва та фотографії знайшли для зображень більш приємні для ока неквадратні пропорції.
![Joseph Nicéphore Niépce (1826)](nicephore.jpg)
Як ми можемо розв'язати цю проблему? Щоб дізнатися як правильно розтягнути текстуру, нам потрібно знати оригінальні пропорції зображення, що дозволить отримати його [*співвідношення сторін*](http://en.wikipedia.org/wiki/Aspect_ratio). Для цього ширина та висота текстури також передаються шейдеру через ```uniform```, який у нашому прикладі буде мати тип ```vec2```. Ми надали цій змінній таку саму назву як і для текстури, але з доповненням ```Resolution```. Отримавши цю інформацію, ми можемо отримати у шейдері співвідношення сторін, поділивши ```ширину``` на ```висоту```. Нарешті, помноживши це співвідношення на координату ```y```, ми змінимо цю вісь таким чином, щоб вона відповідала бажаним пропорціям.
Розкоментуйте рядок 21 наступного коду, щоб побачити це в дії:
<div class="codeAndCanvas" data="texture-resolution.frag" data-textures="nicephore.jpg"></div>
* Поміркуйте, що потрібно зробити, щоб відцентрувати це зображення по центру полотна?
## Цифрова оббивка
![](03.jpg)
Ви можете подумати, що все це трохи ускладнено... й, напевно, матимете рацію. Але такий спосіб роботи з зображеннями залишає достатньо місця для різних хитрощів та творчих прийомів. Спробуйте уявити, що ви робите оббивку, а натягуючи та огортаючи тканину поверх конструкції, ви можете створювати нові патерни та цікавіші результати.
![Eadweard's Muybridge study of motion](muybridge.jpg)
Подібний рівень ремісництва нагадує деякі з перших оптичних експериментів. Наприклад, в іграх дуже поширена *спрайтова анімація*, що навіває спогади про [фенакістископ](https://en.wikipedia.org/wiki/Phenakistiscope), [зоотроп](https://en.wikipedia.org/wiki/Zoetrope) та [праксиноскоп](https://en.wikipedia.org/wiki/Praxinoscope).
Виглядає просто, але можливості для зміни текстурних координат просто величезні.
<div class="codeAndCanvas" data="texture-sprite.frag" data-textures="muybridge.jpg"></div>
Тепер ваша черга:
* Спробуйте зробити калейдоскоп:
<canvas id="custom" class="canvas" data-fragment-url="texture-kaleidoscope.frag" data-textures="hokusai.jpg" width="300px" height="300px"></canvas>
* Задовго до [Oculus](https://en.wikipedia.org/wiki/Oculus_Rift) та [google cardboard](https://en.wikipedia.org/wiki/Google_Cardboard) стереоскопічна фотографія була неабиякою справою. Чи можете ви закодувати простий шейдер, щоб повторно використати ці чудові зображення?
![](texture-stereo-00.jpg)
![](texture-stereo-01.jpg)
![](texture-stereo-03.jpg)
* Які ще оптичні іграшки можна відтворити за допомогою текстур?
У наступних розділах ми побачимо як за допомогою шейдерів можна обробляти зображення, виконуючи певні операції. Зрештою шейдери не в останню чергу розроблені саме для цього.

@ -8,7 +8,7 @@ Graphic cards (GPUs) have special memory types for images. Usually on CPUs image
In order to use this feature we first need to *upload* the image from the CPU to the GPU, to then pass the ```id``` of the texture to the right [```uniform```](../05). All that happens outside the shader.
Once the texture is loaded and linked to a valid ```uniform sampler2D``` you can ask for specific color value at specific coordinates (formated on a [```vec2```](index.html#vec2.md) variable) using the [```texture2D()```](index.html#texture2D.md) function which will return a color formatted on a [```vec4```](index.html#vec4.md) variable.
Once the texture is loaded and linked to a valid ```uniform sampler2D``` you can ask for specific color value at specific coordinates (formatted on a [```vec2```](index.html#vec2.md) variable) using the [```texture2D()```](index.html#texture2D.md) function which will return a color formatted on a [```vec4```](index.html#vec4.md) variable.
```glsl
vec4 texture2D(sampler2D texture, vec2 coordinates)
@ -58,7 +58,7 @@ You may be thinking that this is unnecessarily complicated... and you are probab
![Eadweard's Muybridge study of motion](muybridge.jpg)
This level of craftsmanship links back to some of the first optical experiments ever made. For example on games *sprite animations* are very common, and is inevitably to see on it reminiscence to phenakistoscope, zoetrope and praxinoscope.
This level of craftsmanship links back to some of the first optical experiments ever made. For example on games *sprite animations* are very common, and is inevitably to see on it reminiscence to [phenakistoscope](https://en.wikipedia.org/wiki/Phenakistiscope), [zoetrope](https://en.wikipedia.org/wiki/Zoetrope) and [praxinoscope](https://en.wikipedia.org/wiki/Praxinoscope).
This could seem simple but the possibilities of modifying textures coordinates are enormous. For example:
@ -68,10 +68,13 @@ Now is your turn:
* Can you make a kaleidoscope using what we have learned?
* Way before Oculus or google cardboard, stereoscopic photography was a big thing. Could you code a simple shader to re-use these beautiful images?
<canvas id="custom" class="canvas" data-fragment-url="texture-kaleidoscope.frag" data-textures="hokusai.jpg" width="300px" height="300px"></canvas>
<a href=“../edit.php#10/ikeda-03.frag”><canvas id=“custom” class=“canvas” data-fragment-url=“ikeda-03.frag” width=“520px” height=“200px”></canvas></a>
* Way before [Oculus](https://en.wikipedia.org/wiki/Oculus_Rift) or [google cardboard](https://en.wikipedia.org/wiki/Google_Cardboard), stereoscopic photography was a big thing. Could you code a simple shader to re-use these beautiful images?
![](texture-stereo-00.jpg)
![](texture-stereo-01.jpg)
![](texture-stereo-03.jpg)
* What other optical toys can you re-create using textures?

@ -1,6 +1,7 @@
<?php
$path = "..";
$subtitle = ": Textures";
$README = "README";
$language = "";

@ -0,0 +1,23 @@
## Операції із зображеннями
Нижче ви побачите кілька прикладів із зображеннями, де ви можете пограти зі значеннями та розкоментувати певні рядки, щоб побачити відповідний результат.
### Інвертування
Розкоментуйте рядок 18:
<div class="codeAndCanvas" data="inv.frag" data-textures="00.jpg,01.jpg"></div>
### Додавання, віднімання, множення та інші варіанти
В даному шейдері завантажено два зображення, які розташовано один над одним. По черзі розкоментовуйте рядки 22-27 та спостерігайте за результатами поєднання цих зображень залежно від операції. Перш ніж розкоментувати рядок, поміркуйте над тим який має бути результат і чому:
![](02.jpg)
<div class="codeAndCanvas" data="operations.frag" data-textures="00.jpg,01.jpg"></div>
### Режими змішування як у Photoshop
![](03.jpg)
<div class="codeAndCanvas" data="blend.frag" data-textures="04.jpg,05.jpg"></div>

@ -1,18 +1,19 @@
## Image operations
Below you will see some examples with images where you can play around and uncomment some lines to see the corresponding results.
### Invert
<div class="codeAndCanvas" data="inv.frag" data-imgs="00.jpg,01.jpg"></div>
<div class="codeAndCanvas" data="inv.frag" data-textures="00.jpg,01.jpg"></div>
### Add, Substract, Multiply and others
![](02.jpg)
<div class="codeAndCanvas" data="operations.frag" data-imgs="00.jpg,01.jpg"></div>
<div class="codeAndCanvas" data="operations.frag" data-textures="00.jpg,01.jpg"></div>
### PS Blending modes
![](03.jpg)
<div class="codeAndCanvas" data="blend.frag" data-imgs="04.jpg,05.jpg"></div>
<div class="codeAndCanvas" data="blend.frag" data-textures="04.jpg,05.jpg"></div>

@ -1,6 +1,7 @@
<?php
$path = "..";
$subtitle = ": Image processing";
$README = "README";
$language = "";
@ -25,8 +26,10 @@
<ul class="navigationBar" >
<li class="navigationBar" onclick="previusPage()">&lt; &lt; Previous</li>
<li class="navigationBar" onclick="homePage()"> Home </li>
<li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li>
</ul>';
include($path."/footer.php");
?>
<!-- <li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li> -->

@ -100,6 +100,8 @@ Patricio 研习和实践精神疗法psychotherapy和表达性艺术治疗
感谢 Nahuel Coppero (Necsoft) 的 [西班牙语(español)](?lan=es) 翻译。
感谢 [Manoylov Andriy](https://twitter.com/ManoylovAC) 的 [乌克兰语(українська)](?lan=ua) 翻译。
感谢 [Karim Naaji](http://karim.naaji.fr/) 在代码和想法上的支持和贡献。
感谢所有相信这个项目的人[contributed with fixes](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) 以及大家的捐赠.

@ -97,6 +97,8 @@ Dank an [Andrea Rovescalli](https://www.earove.info) für die [italienische Üb
Dank an [Michael Tischer](http://www.mitinet.de) für die [deutsche Übersetzung des Textes](?lan=de)
Dank an [Manoylov Andriy](https://twitter.com/ManoylovAC) für die [ukrainische Übersetzung des Textes (українська)](?lan=ua)
Und natürlich Danke an alle, die an dieses Projekt geglaubt, dafür gespendet oder durch Hinweise und Korrekturen [daran mitgewirkt haben](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors).
## Hol Dir die neuen Kapitel

@ -83,6 +83,8 @@ Gracias a [Tong Li](https://www.facebook.com/tong.lee.9484) y a [Yi Zhang](https
Gracias a [Jae Hyun Yoo](https://www.facebook.com/fkkcloud) por la [Traducción al coreano (한국어).](?lan=kr)
Gracias a [Manoylov Andriy](https://twitter.com/ManoylovAC) por la [traducción al ucraniano (українська)](?lan=ua)
Gracias a [Karim Naaji](http://karim.naaji.fr/) por su contribución, su apoyo, su código y sus buenas ideas.
Gracias a todos los que creyeron en este proyecto y [contribuyeron con sus aportes](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) o donaciones.
@ -97,4 +99,4 @@ Suscribirse al newsletter o seguirnos en [Twitter](https://twitter.com/bookofsha
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
</script>

@ -102,6 +102,8 @@
ممنون از [Sergey Karchevsky](https://www.facebook.com/sergey.karchevsky.3) برای Russian [translation (russian)](?lan=ru)
ممنون از [Manoylov Andriy](https://twitter.com/ManoylovAC) برای Ukrainian [translation (українська)](?lan=ua)
ممنون از [Andy Stanton](https://andy.stanton.is/) برای اصلاح و بهبود [the pdf/epub export pipeline](https://thebookofshaders.com/appendix/02/)
ممنون از همه کسانی که به این پروژه ایمان داشتند و[در اصلاحات مشارکت کردند](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) و یا اهدا کردند.
@ -116,4 +118,4 @@
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
</script>

@ -85,6 +85,8 @@ Merci à [Tong Li](https://www.facebook.com/tong.lee.9484) et [Yi Zhang](https:/
Merci à [Jae Hyun Yoo](https://www.facebook.com/fkkcloud) pour la [traduction (한국어)](?lan=kr) coréenne
Merci à [Manoylov Andriy](https://twitter.com/ManoylovAC) pour la [traduction (українська)](?lan=ua) l'ukrainien
Merci à [Karim Naaji](http://karim.naaji.fr/) qui a contribué par son support, ses bonnes idées et son code.
Merci à tous ceux qui ont cru en ce projet et à ceux qui ont [contributé aux corrections](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) ou qui ont fait des dons.

@ -106,6 +106,8 @@ Terima kasih kepada [Andy Stanton](https://andy.stanton.is/) untuk perbaikan dan
Terima kasih kepada [Naufal Adriansyah](https://www.facebook.com/naufal.adrna08) untuk terjemahan [Bahasa Indonesia](?lan=id)
Terima kasih kepada [Manoylov Andriy](https://twitter.com/ManoylovAC) untuk terjemahan [Bahasa Ukraina (українська)](?lan=ua)
Terima kasih kepada semua orang yang telah percaya pada proyek ini dan [telah berkontribusi dalam perbaikan](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) atau donasi.
## Dapatkan bagian baru
@ -118,4 +120,4 @@ Daftar untuk surat berita atau follow di [Twitter](https://twitter.com/bookofsha
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
</script>

@ -96,6 +96,8 @@ Grazie a [Nicolas Barradeau](https://twitter.com/nicoptere) e a [Karim Naaji](ht
Grazie a [Andrea Rovescalli](https://www.earove.info) per la [traduzione](?lan=it) italiana
Grazie a [Manoylov Andriy](https://twitter.com/ManoylovAC) per la [traduzione ucraina (українська)](?lan=ua)
Grazie a tutti coloro i quali hanno creduto in questo progetto e [contribuito con correzioni](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) o donazioni.
## Come ottenere i nuovi capitoli?

@ -89,6 +89,8 @@ Patricioは心理療法psychotherapyと表現療法expressive art thera
インスピレーションとアドバイスを与えてくれた[Scott Murray](http://alignedleft.com/)、日本語訳を担当してくれた[Kynd](https://twitter.com/kyndinfo)、素晴らしいアイデアとコードで貢献してくれた[Karim Naaji](http://karim.naaji.fr/) にも感謝します。
[(українська) ウクライナ語](?lan=ua)の翻訳をしてくれた [Manoylov Andriy](https://twitter.com/ManoylovAC) にも感謝します。
そして最後に、このプロジェクトを応援し[改善の手助けをしてくれた方々](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors)、寄付をくださったすべての皆様に感謝を述べたいと思います。
## アップデートのお知らせ

@ -108,6 +108,8 @@ Patricio는 심리치료 및 표현예술을 공부했다. 그는 파슨스대
러시아어 번역을 맡고 있는 [Sergey Karchevsky](https://www.facebook.com/sergey.karchevsky.3) 에게 감사를 표합니다. [Russian translation](?lan=ru)
러시아어 번역을 맡고 있는 [Manoylov Andriy](https://twitter.com/ManoylovAC) 에게 감사를 표합니다. [Ukrainian translation (українська)](?lan=ua)
[pdf/epub 배포](https://thebookofshaders.com/appendix/02/) 수정 및 개선을 맡고 있는 [Andy Stanton](https://andy.stanton.is/) 에게 감사를 표합니다.
이 프로젝트를 격려해주시고 기부해주신 모든 분들께 감사를 드립니다. [Contributed with fixes](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors)
@ -122,4 +124,4 @@ Patricio는 심리치료 및 표현예술을 공부했다. 그는 파슨스대
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
</script>

@ -106,6 +106,8 @@ Podziękowania dla [Vu Phuong Hoang](https://www.facebook.com/vuphuonghoang88) z
Podziękowania dla [Wojciecha Pachowiaka](https://github.com/WojtekPachowiak) za polskie [tłumaczenie (polski)](?lan=pl)
Podziękowania dla [Manoylov Andriy](https://twitter.com/ManoylovAC) za ukraińskie [tłumaczenie (українська)](?lan=ua)
Podziękowania dla [Andy Stanton](https://andy.stanton.is/) za naprawę i usprawnienie funkcji [eksportu pdf/epub ](https://thebookofshaders.com/appendix/02/)
Podziękowania dla każdego, kto [współtworzy](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) ten projekt poprzez swoje rady, korekty lub finansowe wsparcie.

@ -100,6 +100,8 @@ Obrigado [Michael Tischer](http://www.mitinet.de) pela [tradução em Alemão (d
Obrigado [Sergey Karchevsky](https://www.facebook.com/sergey.karchevsky.3) pela [tradução em Russo (russian)](?lan=ru)
Obrigado [Manoylov Andriy](https://twitter.com/ManoylovAC) pela [tradução em Ucraniano (українська)](?lan=ua)
Obrigado [Andy Stanton](https://andy.stanton.is/) por corrigir e melhorar [a pipeline para exportar pdf/epub](https://thebookofshaders.com/appendix/02/)
Obrigado a todos que acreditaram neste projeto e [contribuíram com correções](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) ou doações.
@ -114,4 +116,4 @@ Assine a newsletter ou nos siga no [Twitter](https://twitter.com/bookofshaders)
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
</script>

@ -100,6 +100,8 @@
Спасибо [Сергею Карчевскому](https://www.facebook.com/sergey.karchevsky.3) за [русский перевод](?lan=ru)
Спасибо [Андрею Манойлову](https://twitter.com/ManoylovAC) за [украинский перевод](?lan=ua)
Спасибо всем кто поверил в этот проект и поддержал его [исправлениями](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) или пожертвованиями.
## Новые параграфы

@ -0,0 +1,130 @@
<canvas id="custom" class="canvas" data-fragment-url="src/moon/moon.frag" data-textures="src/moon/moon.jpg" width="350px" height="350px"></canvas>
# The Book of Shaders
*автори: [Patricio Gonzalez Vivo](http://patriciogonzalezvivo.com/) і [Jen Lowe](http://jenlowe.net/)*
Легкий покроковий провідник через абстрактний і складний всесвіт фрагментних шейдерів.
<div class="header">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B5FSVSHGEATCG" style="float: right;"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt=""></a>
</div>
## Зміст
* [Про книгу](00/?lan=ua)
* Вступ
* [Що таке шейдер?](01/?lan=ua)
* [“Hello world!”](02/?lan=ua)
* [Uniforms](03/?lan=ua)
* [Запуск шейдера](04/?lan=ua)
* Алгоритмічне малювання
* [Формотворчі функції](05/?lan=ua)
* [Кольори](06/?lan=ua)
* [Фігури](07/?lan=ua)
* [Матриці](08/?lan=ua)
* [Патерни](09/?lan=ua)
* Генеративний дизайн
* [Випадковість](10/?lan=ua)
* [Шум](11/?lan=ua)
* [Клітинний шум](12/?lan=ua)
* [Фрактальний броунівський рух](13/?lan=ua)
* Фрактали
* Обробка зображень
* Текстури
* Операції із зображеннями
* Згортка ядра
* Фільтри
* Інші ефекти
* Симуляції
* Пінг-понг
* Conway
* Брижі води
* Акварель
* Реакційно-дифузійна система
* 3D-графіка
* Освітлення
* Карти нормалей
* Карти висот
* Ray marching
* Карти оточення (сферичні та кубічні)
* Відображення та заломлення
* [Додаток:](appendix/?lan=ua) Інші способи використання цієї книги
* [Як можна переглядати книгу офлайн?](appendix/00/?lan=ua)
* [Як запустити приклади на Raspberry Pi?](appendix/01/?lan=ua)
* [Як надрукувати книгу?](appendix/02/?lan=ua)
* [Як прийняти участь у розвитку книги?](appendix/03/?lan=ua)
* [Вступ для тих, хто прийшов із JS](appendix/04/?lan=ua) від [Nicolas Barradeau](http://www.barradeau.com/)
* [Галерея прикладів](examples/?lan=ua)
* [Глосарій](glossary/?lan=ua)
## Про авторів
[Patricio Gonzalez Vivo](http://patriciogonzalezvivo.com/) (1982, Буенос-Айрес, Аргентина) — митець і розробник із Нью-Йорка. Досліджує інтерстиціальний простір між органічним і синтетичним, аналоговим і цифровим, індивідуальним і колективним. У своїй роботі він використовує код як виразну мову з метою створення кращого разом.
Патрісіо вивчав і практикував психотерапію та арттерапію. Він здобув ступінь магістра мистецтва в галузі дизайну та технологій у Parsons The New School, де зараз і викладає. Наразі він працює як графічний інженер в компанії Mapzen, розробляючи open-source інструменти для картографування.
<div class="header"> <a href="http://patriciogonzalezvivo.com/" target="_blank">WebSite</a> - <a href="https://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
[Jen Lowe](http://jenlowe.net/) - незалежна наукова фахівчиня у галузі обробки та передачі даних в компанії Datatelling, де вона поєднує людей, числа та слова. Вона викладає у SVA Design для програми соціальних інновацій, співзасновниця School for Poetic Computation, викладала математику для художників в NYU ITP, проводила дослідження в Лабораторії просторового інформаційного дизайну у Колумбійському університеті та допомагала з ідеями в Білому домі стосовно управління науки та технологічної політики. Вона виступала на конференціях SXSW та Eyeo. Її роботи висвітлювалися в The New York Times та Fast Company. У своїх дослідженнях, публікаціях та виступах вона досліджує перспективи та наслідки використання даних і технологій у суспільстві. Має ступінь бакалавра з прикладної математики та магістра інформаційних наук. Часто займаючи опозиційну позицію, вона завжди на боці любові.
<div class="header"> <a href="http://jenlowe.net/" target="_blank">WebSite</a> - <a href="https://twitter.com/datatelling" target="_blank">Twitter</a> - <a href="https://github.com/datatelling" target="_blank">GitHub</a></div>
## Подяки
Дякую [Scott Murray](http://alignedleft.com/) за натхнення та поради.
Дякую [Kenichi Yoneda (Kynd)](https://twitter.com/kyndinfo), [Nicolas Barradeau](https://twitter.com/nicoptere), [Karim Naaji](http://karim.naaji.fr/) за підтримку, гарні ідеї та код.
Дякую [Kenichi Yoneda (Kynd)](https://twitter.com/kyndinfo) і [Sawako](https://twitter.com/sawakohome) за [японський переклад (日本語訳)](?lan=jp)
Дякую [Tong Li](https://www.facebook.com/tong.lee.9484) і [Yi Zhang](https://www.facebook.com/archer.zetta?pnref=story) за [китайський переклад (中文版)](?lan=ch)
Дякую [Jae Hyun Yoo](https://www.facebook.com/fkkcloud) і [June Kim](https://github.com/rlawns324) за [корейський переклад (한국어)](?lan=kr)
Дякую Nahuel Coppero (Necsoft) за [іспанський переклад (español)](?lan=es)
Дякую [Raphaela Protásio](https://github.com/Rawphs) і [Lucas Mendonça](https://github.com/luuchowl) за [португальський переклад (portugues)](?lan=pt)
Дякую [Nicolas Barradeau](https://twitter.com/nicoptere) і [Karim Naaji](http://karim.naaji.fr/) за [французький переклад (français)](?lan=fr)
Дякую [Andrea Rovescalli](https://www.earove.info) за [італійський переклад (italiano)](?lan=it)
Дякую [Michael Tischer](http://www.mitinet.de) за [німецький переклад (deutsch)](?lan=de)
Дякую [Sergey Karchevsky](https://www.facebook.com/sergey.karchevsky.3) за [російський переклад](?lan=ru)
Дякую [Vu Phuong Hoang](https://www.facebook.com/vuphuonghoang88) за [в'єтнамський переклад (Tiếng Việt)](?lan=vi)
Дякую [Wojciech Pachowiak](https://github.com/WojtekPachowiak) за [польський переклад (polski)](?lan=pl)
Дякую [Манойлову Андрію](https://twitter.com/ManoylovAC) за [український переклад](?lan=ua)
Дякую [Andy Stanton](https://andy.stanton.is/) за виправлення та вдосконалення [the pdf/epub export pipeline](https://thebookofshaders.com/appendix/02/?lan=ua)
Дякую всім, хто повірив у цей проект і [підтримував його виправленнями](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) або пожертвуваннями.
## Нові розділи
Для оповіщення про нові розділи, підпишіться на поштову розсилку або на [Twitter](https://twitter.com/bookofshaders) / <a rel="me" href="https://mastodon.gamedev.place/@bookofshaders">Mastodon</a> / [Discord](shader.zone)
<div id="fd-form-623359074e5181d777e479f9"></div>
<script>
window.fd('form', {
formId: '623359074e5181d777e479f9',
containerEl: '#fd-form-623359074e5181d777e479f9'
});
</script>
## LICENSE
Copyright (c) Patricio Gonzalez Vivo, 2015 - http://patriciogonzalezvivo.com/
All rights reserved.

@ -104,6 +104,8 @@ Cảm ơn [Sergey Karchevsky](https://www.facebook.com/sergey.karchevsky.3) vì
Cảm ơn [Vu Phuong Hoang](https://github.com/DancingPhoenix88) và [Minh-Phuc Bui](https://github.com/phucbm) vì [Bản dịch tiếng Việt](?lan=vi)
Cảm ơn [Manoylov Andriy](https://twitter.com/ManoylovAC) vì [Bản dịch tiếng Ukraina (українська)](?lan=ua)
Cảm ơn [Andy Stanton](https://andy.stanton.is/) vì đã sửa lỗi và cải tiến [cách export quyển sách ra định dạng pdf/epub](https://thebookofshaders.com/appendix/02/?lan=vi)
Cảm ơn tất cả mọi người đã tin tưởng, [cùng sửa lỗi](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) và quyên góp cho dự án này.

@ -106,6 +106,8 @@ Thanks [Vu Phuong Hoang](https://www.facebook.com/vuphuonghoang88) for the Vietn
Thanks [Wojciech Pachowiak](https://github.com/WojtekPachowiak) for the Polish [translation (polski)](?lan=pl)
Thanks [Manoylov Andriy](https://twitter.com/ManoylovAC) for the Ukrainian [translation (український переклад)](?lan=ua)
Thanks [Andy Stanton](https://andy.stanton.is/) for fixing and improving [the pdf/epub export pipeline](https://thebookofshaders.com/appendix/02/)
Thanks to everyone who has believed in this project and [contributed with fixes](https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors) or donations.

@ -0,0 +1,33 @@
## Як користуватись книгою офлайн?
Припустимо, у вас довга подорож і ви хочете за цей час повчити шейдери. У такому випадку ви можете скопіювати книгу на свій комп'ютер і запустити її на локальному сервері.
Для цього вам потрібні лише PHP, Python 3 і git-клієнт. На комп'ютерах MacOS і Raspberry Pi Python встановлено за замовчуванням, але потрібно ще встановити PHP і git-клієнт. Це можна зробити наступним чином:
Для **MacOSX** переконайтесь, що у вас встановлено [homebrew](http://brew.sh/), після чого в терміналі виконайте наступні команди:
```bash
brew update
brew upgrade
brew install git php
```
На **Raspberry Pi** встановіть [Raspbian](https://www.raspberrypi.org/downloads/raspbian/), дистрибутив Linux для Raspberry Pi на основі Debian, а потім виконайте наступні команди:
```bash
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer php
```
Після того, як все буде встановлено, склонуйте проєкт та запустіть локальний сервер:
```bash
cd ~
git clone --recursive https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
git submodule foreach git submodule init && git submodule update
php -S localhost:8000
```
Потім відкрийте у своєму браузері адресу [`http://localhost:8000/`](http://localhost:8000/)

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": How can I navigate this book off-line?";
$README = "README";
$language = "";

@ -0,0 +1,18 @@
## Як запустити приклади на Raspberry Pi?
Кілька років тому, припускати, що кожен має комп'ютер із графічним процесором, було важко. Зараз більшість комп'ютерів вже мають GPU, хоча подекуди й для деяких навчальних класів це все ще не так.
Завдяки [Raspberry Pi Foundation](http://www.raspberrypi.org/) новий тип невеликих і дешевих комп'ютерів (близько 35$ за штуку) знайшов свій шлях до навчальних класів. Що ще важливіше для цілей цієї книги, [Raspberry Pi](http://www.raspberrypi.org/) постачається з гідним графічним процесором Broadcom, до якого можна отримати доступ безпосередньо з консолі. Я створив гнучкий інструмент для онлайн-кодування GLSL під назвою [**glslViewer**](https://github.com/patriciogonzalezvivo/glslViewer), який запускає всі приклади цієї книги. Ця програма має можливість автоматично оновлювати результат, коли користувач зберігає зміни у своєму коді. Що це значить? Ви можете редагувати код шейдеру, і щоразу, коли ви його зберігаєте, він буде автоматично скомпільований з оновленням вислідного зображення.
Створивши локальну копію репозиторію книги (див. [попередній розділ](../00/?lan=ua)) і встановивши [`glslViewer`](https://github.com/patriciogonzalezvivo/glslViewer), можна запускати приклади за допомогою команди `glslviewer`. Крім того, з додатковим прапорцем `-l` можна візуалізувати поточний приклад у кутку екрана під час редагування коду за допомогою будь-якого текстового редактора (наприклад, `nano`, `pico`, `vi`, `vim` або `emacs`) . Це також працює при підключенні через ssh/sftp.
Щоб встановити та налаштувати все необхідне на Raspberry Pi, після встановлення [Raspbian](https://www.raspberrypi.org/downloads/raspbian/) і входу в систему, введіть наступні команди:
```bash
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
```

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": How to run the examples on a Raspberry Pi?";
$README = "README";
$language = "";

@ -0,0 +1,72 @@
## Як надрукувати цю книгу?
Скажімо, вам не потрібна навігація по тексту чи взаємодія з інтерактивними прикладами, а просто потрібна книга, яку ви зможете читати на пляжі або під час поїздок. У такому випадку ви можете надрукувати її.
#### Встановлення glslViewer
Щоб надрукувати цю книгу, її потрібно спочатку розпарсити. Для цього вам знадобиться [`glslViewer`](https://github.com/patriciogonzalezvivo/glslViewer) - консольний інструмент, який компілює та перетворює приклади шейдерів у зображення.
На **MacOSX** переконайтесь, що у вас встановлено [homebrew](http://brew.sh/) і потім виконайте у терміналі наступну команду:
```bash
brew install glslviewer
```
На **Raspberry Pi** установіть [Raspbian](https://www.raspberrypi.org/downloads/raspbian/) - дистрибутив Linux на основі Debian, створений для Raspberry Pi, а потім виконайте такі дії:
```bash
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git-core glslviewer
```
#### Встановлення Python 3, Latex Engine і Pandoc
Для розбору розділів Markdown-розмітки у Latex, а потім у PDF-файл, ми будемо використовувати Xetex Latex Engine і Pandoc.
На **MacOSX**:
Завантажте та встановіть MacTeX:
```bash
brew install --cask mactex-no-gui
```
а потім інсталюйте [Pandoc](http://johnmacfarlane.net/pandoc/) і Python 3 за допомогою команди:
```bash
brew install pandoc python
```
На **Raspberry Pi** (Raspbian):
```bash
sudo apt-get install texlive-xetex pandoc python2.7
```
#### Зберіть книгу у pdf-формат та роздрукуйте її
Тепер, коли у вас є все необхідне, настав час клонувати [репозиторій цієї книги](https://github.com/patriciogonzalezvivo/thebookofshaders) та скомпілювати його.
Для цього ще раз відкрийте термінал і виконайте наступні команди:
```bash
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean pdf
```
Якщо все пройде добре, ви побачите файл `book.pdf`, який можна прочитати на своєму улюбленому пристрої або роздрукувати.
#### Зберіть книгу в epub-формат для використання з Kindle
```bash
cd ~
git clone https://github.com/patriciogonzalezvivo/thebookofshaders.git
cd thebookofshaders
make clean epub
```
Згенерований файл `book.epub` можна використовувати безпосередньо або конвертувати у формат `.mobi` для використання з Kindle за допомогою конвертера, наприклад Calibre.

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": How to print this book?";
$README = "README";
$language = "";

@ -0,0 +1,63 @@
## Як прийняти участь у розвитку книги?
Дякуємо за бажання співпрацювати! Для цього існує кілька способів:
- Переклад контенту
- Покращення та допрацювання [```глосарію```](https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/glossary)
- Редагування контенту
- Діліться своїми прикладами шейдерів через [онлайн-редактор](http://editor.thebookofshaders.com/)
### Переклад контенту
Ця книга написана мовою розмітки [Markdown](https://daringfireball.net/projects/markdown/syntax), тому її дуже легко редагувати та працювати з нею.
1. Для початку перейдіть до github-репозиторію за адресою [```github.com/patriciogonzalezvivo/thebookofshaders```](https://github.com/patriciogonzalezvivo/thebookofshaders). Перегляньте в ньому файли та теки. Ви помітите, що контент знаходиться в ```README.md``` та інших файлах з назвами із великих літер, наприклад: ```TITLE.md```, ```SUMMARY.md``` тощо. Також зауважте, що переклади розміщуються у файлах, імена яких закінчуються додатковими двома літерами, що вказують на мову перекладу, наприклад: ```README-jp.md```, ```README-es.md``` тощо.
2. Зробіть відгалуження репозиторію і клонуйте його на свій комп'ютер.
3. Продублюйте вміст файлів, які потрібно перекласти. Додайте до них двобуквенний код, що буде вказувати на мову для якої ви робите переклад.
4. Перекладіть вміст файлу (див. **Примітки щодо перекладу**).
5. Протестуйте зміни (див. **Тестування**).
6. Зробіть push у ваш власний форк проєкту і створіть [pull request](https://help.github.com/articles/using-pull-requests/)
#### Примітки щодо перекладу
Не видаляйте та не змінюйте вбудовані приклади, які виглядають приблизно так:
```html
<div class="codeAndCanvas" data="grid-making.frag"></div>
```
Або так:
```html
<div class="simpleFunction" data="y = mod(x,2.0);"></div>
```
#### Тестування
Запустіть локальний PHP-сервер у теці локального репозиторію:
```bash
php -S localhost:8000
```
Тепер у вашому браузері зайдіть на адресу [```localhost:8000```](http://localhost:8000), перейдіть до розділу, який ви перекладаєте, і додайте в кінці адреси такий рядок ```?lan=```, а після нього дві літери для позначення мови перекладу.
Наприклад, якщо ви перекладаєте розділ ```03``` на французьку мову, значить ви працювали з файлом ```03/README-fr.md```, і ви можете перевірити його за адресою: ``` http://localhost:8000/03/?lan=fr```
### Покращення глосарію
Цей розділ знаходиться в розробці. Ми раді вислухати ваші ідеї щодо його покращення, щоб зробити його корисним інструментом для всіх. Надсилайте свої пропозиції на [@bookofshaders](https://twitter.com/bookofshaders).
### Редагування контенту
Ми всі люди. Якщо ви побачите помилку, то сповістіть про неї, зробіть pull-request з виправленням або відкрийте issue. Дякую!
### Діліться своїми прикладами шейдерів
Ви побачите багато посилань на [онлайн-редактор](http://editor.thebookofshaders.com/) і його вбудовані у сторінки приклади з кодом.
Коли ви закодуєте щось корисне чи цікаве, натисніть «Export» (або піктограму ```⇪```) і надішліть його на адресу [@bookofshaders](https://twitter.com/bookofshaders) або [@kyndinfo](https://twitter.com/kyndinfo). Ми будемо раді переглянути його і додати до [галереї прикладів](https://thebookofshaders.com/examples/).

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": How can I collaborate with this book?";
$README = "README";
$language = "";

@ -0,0 +1,449 @@
## Вступ для тих, хто прийшов із JS
автор [Nicolas Barradeau](http://www.barradeau.com/)
Якщо ви JavaScript-розробник, швидше за все, ви будете трохи спантеличені, читаючи книгу.
Дійсно, є багато відмінностей між маніпулюванням високорівневим JS і длубанням у менш високорівневих шейдерах.
Проте, на відміну від більш низькорівневої мови асемблера, GLSL є людино-зрозумілою мовою, і я впевнений, що як тільки ви розберетеся з її особливостями, то швидко запрацюєте.
Я припускаю, що ви маєте хоча б поверхневе знання про JavaScript і API Canvas.
Якщо ні, не хвилюйтеся, ви все одно зможете зрозуміти більшу частину цього розділу.
Крім того, я не буду надто вдаватися в подробиці, і деякі речі можуть бути апівправдою_, тож не розраховувайте на "вичерпний посібник", а радше на ознайомлення.
### Введення для новачків
JavaScript чудово підходить для швидкого прототипування; ви накидуєте купу різноманітних нетипізованих змінних і методів, можете динамічно додавати та видаляти члени класів, оновлювати сторінку і швидко перевіряти чи вона працює.
Потім можна внести нові зміни, оновити сторінку, повторити - легке життя.
Тож ви можете поставити собі питання, у чому різниця між JavaScript і GLSL?
Зрештою, обидва працюють у браузері, обидва використовуються для малювання купи прикольних штук на екрані, і в цьому сенсі JS легше використовувати.
Що ж, головна відмінність полягає в тому, що Javascript є **інтерпретованою** мовою, тоді як GLSL є **компільованою**.
**Скомпільована** програма виконується нативно в ОС, є низькорівневою і загалом швидкою.
**Інтерпретована** програма потребує для свого виконання [віртуальну машину](https://en.wikipedia.org/wiki/Virtual_machine) (VM), є високорівневою і відносно більш повільною.
Коли браузер (_**VM** для JavaScript_) **виконує** або **інтерпретує** фрагмент JS-коду, він ще не має уявлення про те, яка змінна чим являється і яка функція що робить (за винятком **типізованих масивів**).
Тож він не може нічого оптимізувати аперед_.
Потрібен деякий час, щоб прочитати ваш код, щоб **вивести**, на основі використання, типи ваших змінних та методів і, коли це можливо, перетворити ещо_ з вашого коду на код асемблера, який виконуватиметься набагато швидше.
Це повільний, кропіткий і шалено складний процес. Якщо вас цікавлять деталі, я рекомендую подивитися, як [працює рушій V8 у Chrome](https://developers.google.com/v8/).
Найгірше те, що кожен браузер оптимізує JS по-своєму, а процес _прихований_ від вас. В цьому плані ви безсилі.
**Компільована** програма не потребує інтерпретації. Операційна система запускає її і якщо програма валідна, вона виконується.
Це велика відмінність. Якщо ви забули крапку з комою в кінці рядка, ваш код вже не валідний та не скомпілюється, він взагалі не перетвориться на програму.
Це суворо, але це те, чим є **шейдер**: _програмою, що компілюється для виконання на GPU_.
Не бійтеся! **Компілятор** який перевіряє правильність вашого коду, стане вашим найкращим другом.
Приклади цієї книги та [редактор коду](http://editor.thebookofshaders.com/) дуже дружні до користувача.
Вони підкажуть вам де і чому не вдалося скомпілювати вашу програму.
Потім, після потрібних виправлень, коли шейдер буде готовий до компіляції, він миттєво зобразить результат своєї роботи. Це чудовий спосіб навчання, оскільки він дуже наочний і безпечний, бо насправді ви нічого не зможете зламати.
Останнє зауваження: **шейдер** складається з 2 програм: **вершинного шейдера** і **фрагментного шейдера**.
Якщо коротко, то **вершинний шейдер** це перша програма, що отримує на вхід *геометрію* та перетворює її на серію **пікселів** (або *фрагментів*).
Потім вона передає їх до **фрагментного шейдера** - другої програми, яка вирішить, яким кольором пофарбувати ці пікселі.
Дана книга здебільшого зосереджена на **фрагментних шейдерах**. У всіх прикладах геометрія є простим чотирикутником, який охоплює весь екран.
Готові?
Поїхали!
### Сильні типи
![перший результат пошуку для 'strong type' в Google Image на 20.05.2016](strong_type.jpg)
Коли ви приходите з JS або будь-якої іншої нетипізованої мови, **типізація** змінних являється для вас чужорідною концепцією, що стає найважчим кроком до GLSL.
**Типізація**, як випливає з назви, означає, що вам потрібно надавати **тип** усім своїм змінним і функціям.
Це фактично означає, що слів **`var`** або **`let`** більше не існує.
Поліція думок GLSL стерла їх із загальної мови й ви більше не можете їх використовувати, тому що, ну... їх не існує.
Замість використання чарівного слова **`var`** вам доведеться _явно вказати тип кожної змінної_, яку ви використовуєте, тоді компілятор бачитиме лише ті об'єкти та примітиви, з якими він вміє ефективно поводитися.
Мінус, що ви не можете використовувати ключове слово **`var`** і повинні _явно вказувати всі типи_, полягає в тому, що вам доведеться знати типи усіх змінних і знати їх добре.
Будьте спокійні, їх небагато і вони досить прості (GLSL це вам не Java-фреймворк).
Це може здатися страшним, але загалом це не дуже відрізняється від того, що ви робите на JavaScript. Наприклад, якщо ви маєте `boolean`-змінну, то ви очікуєте, що вона зберігатиме лише `true` або `false` і нічого більше.
Якщо змінна називається `var uid = XXX;`, то в ній вірогідно зберігається цілочисельне значення, а `var y = YYY;` оже_ бути посиланням на значення з рухомою крапкою.
Що ще краще, завдяки **сильним типам** ви не витрачатимете час на роздуми про те, чи `X == Y` (чи `typeof X == typeof Y`?, чи `typeof X !== null && Y...`). Ви просто *знаєте* це, а якщо ні, то компілятор знатиме напевно.
Ось **скалярні типи** (**скаляр** описує величину), які можна використовувати в GLSL: `bool` (булів тип), `int` (ціле число), `float` (число з рухомою крапкою).
Є й інші типи, але не будемо поспішати. Наступний фрагмент показує, як оголосити **`vars`** (так, я використав заборонене слово) у GLSL:
```glsl
// логічне значення
JS: var b = true; GLSL: bool b = true;
// цілочисельне значення
JS: var i = 1; GLSL: int i = 1;
// числове значення з рухомою крапкою
JS: var f = 3.14159; GLSL: float f = 3.14159;
```
Не дуже важко, правда? Як згадувалося вище, це навіть полегшує роботу, оскільки ви не витрачаєте час на перевірку типів даних змінних.
Якщо це здається сумнівним, то пам'ятайте, що ви робите це для того, щоб ваша програма виконувалася набагато швидше, ніж на JS.
#### void
Тип `void` приблизно відповідає `null`. Він використовується в якості типу, який має повертати метод, коли він нічого не повертає.
Ви не можете призначати його змінним.
#### boolean
Як вам відомо, логічні значення здебільшого використовуються в умовних перевірках: "`if (myBoolean == true) {...} else {...}`".
Якщо умовне розгалуження є звичайним підходом для CPU, то для [паралельної природи](http://thebookofshaders/01/?lan=ua) GLSL це твердження є менш правдивим.
Використання умовних галужень навіть не рекомендується у більшості випадків і у книзі показано кілька альтернативних методів для вирішення цього обмеження.
#### приведення типів
Як казав [Боромир](https://en.wikipedia.org/wiki/Boromir), "не можна просто взяти та скомбінувати типізовані примітиви". На відміну від JavaScript, GLSL не дозволить вам виконувати операції між змінними різних типів.
Наприклад, цей код:
```glsl
int i = 2;
float f = 3.14159;
// спроба помножити ціле число на значення з рухомою крапкою
float r = i * f;
```
не спрацює, тому що ви намагаєтеся схрестити **_кота_** і **_жирафа_**.
Проблема вирішується за допомогою **приведення типу**, що _змусить компілятор повірити_, що *`i`* має тип `float` без фактичної зміни типу *`i`*.
```glsl
// приведення типу цілочисельної змінної 'i' до float
float r = float(i) * f;
```
Це як вдягти **_кота_** у **шкіру _жирафа_** і працюватиме належним чином (змінна `r` зберігатиме результат множення `i` на `f`).
Можна **привести** будь-який зі згаданих вище типів до будь-якого іншого. Зауважте, що приведення `float` до `int` поводитиметься як `Math.floor()`, оскільки видалятиме значення після рухомої крапки.
Приведення `float` або `int` до `bool` поверне `true`, якщо змінна не дорівнює нулю.
#### конструктор
**Типи** змінних також є **конструкторами класів** для самих себе. Змінну `float` фактично можна розглядати як _`екземпляр`_ класу _`float`_.
Наступні оголошення рівнозначно валідні:
```glsl
int i = 1;
int i = int(1);
int i = int(1.9995);
int i = int(true);
```
Це може здатися не дуже схожим на `скалярні` типи та не дуже відрізняється від **приведення типів**, але у цьому з'явиться більше сенсу, коли дійдемо до розділу *перевантаження*.
Отже, ми познайомилися з трьома `примітивними типами`, без яких ви не зможете жити, але, звісно, GLSL має й інші.
### Вектори
![перший результат пошуку для 'vector villain' у Google Image на 20.05.2016](vector.jpg)
У Javascript, як і в GLSL, вам знадобляться складніші способи маніпуляції даними, ось де **`вектори`** стануть у пригоді.
Я припускаю, що вам вже доводилося писати клас `Point` на JavaScript, для утримання значень `x` і `y`, що виглядав якось так:
```glsl
// визначення класу:
var Point = function(x, y) {
this.x = x || 0;
this.y = y || 0;
}
// створення екземляру:
var p = new Point(100, 100);
```
Як ми щойно побачили, це ДУЖЕ неправильно на ДУЖЕ багатьох рівнях! По-перше, ключове слово **`var`**, далі жахливе **`this`**, потім знову **нетипізовні** значення `x` і `y`...
Ні, це не запрацює в шейдерленді.
Натомість GLSL надає вбудовані структури даних для їх групування, а саме:
* **`bvec2`**: 2D вектор для bool, **`bvec3`**: 3D вектор для bool, **`bvec4`**: 4D вектор для bool
* **`ivec2`**: 2D вектор для int, **`ivec3`**: 3D вектор для int, **`ivec4`**: 4D вектор для int
* **`vec2`**: 2D вектор для float, **`vec3`**: 3D вектор для float, **`vec4`**: 4D вектор для float
Ви відразу помітили, що для кожного примітиву є відповідний **векторний** тип.
З показаного вище, ви можете зробити висновок, що `bvec2` буде містити два значення типу `bool`, а `vec4` — чотири значення `float`.
Також вектори вводять таку річ як **вимірність** або **розмірність**. Це не означає, що 2D-вектор використовується при малюванні 2D-графіки, а 3D-вектор при малюванні 3D-сцен. Ні!
Що в такому разі буде представляти 4D-вектор? (ну насправді це називається тесерактом або гіперкубом)
**Розмірність** представляє кількість і тип **компонентів** або **змінних**, що зберігаються у **векторі**:
```glsl
// створюємо двовимірний булів вектор
bvec2 b2 = bvec2(true, false);
// створюємо тривимірний цілочисельний вектор
ivec3 i3 = ivec3(0, 0, 1);
// створюємо чотиривимірний вектор з рухомою комою
vec4 v4 = vec4(0.0, 1.0, 2.0, 1.0);
```
`b2` зберігає два різних булевих значення, `i3` - 3 різні цілочисельні значення, а `v4` - 4 різні значення з рухомою комою.
Але як звернутися до цих значень?
У випадку `скалярів` відповідь очевидна: для "`float f = 1.2;`", змінна `f` містить значення `1.2`.
З **векторами** це трохи інакше і доволі красиво.
#### аксесори - доступи до елементів
Існують різні способи доступу до значень
```glsl
// створимо чотиривимірний вектор типу float
vec4 v4 = vec4(0.0, 1.0, 2.0, 3.0);
```
отримати кожне з 4-х значень, можна наступним чином:
```glsl
float x = v4.x; // x = 0.0
float y = v4.y; // y = 1.0
float z = v4.z; // z = 2.0
float w = v4.w; // w = 3.0
```
Просто і легко. Наведені нижче приклади показують інші еквівалентні способи доступу до цих даних:
```glsl
float x = v4.x = v4.r = v4.s = v4[0]; // x = 0.0
float y = v4.y = v4.g = v4.t = v4[1]; // y = 1.0
float z = v4.z = v4.b = v4.p = v4[2]; // z = 2.0
float w = v4.w = v4.a = v4.q = v4[3]; // w = 3.0
```
Кмітливий читач міг помітити три речі:
* `X`, `Y`, `Z`, `W` зазвичай використовуються в 3D-програмах для представлення 3D-векторів
* `R`, `G`, `B`, `A` використовуються для кодування кольорів і альфа-каналу
* `[0]`, `[1]`, `[2]`, `[3]` означає, що ми можемо звертатися до значень через індекси
Тож залежно від того, чи працюєте ви з дво- чи тривимірними координатами, кольором з альфа-каналом чи без нього, або просто з деякими довільними значеннями, ви можете вибрати найбільш відповідний тип і розмірність **вектора**.
Зазвичай двовимірні координати та вектори (в геометричному сенсі) зберігаються як `vec2`, `vec3` або `vec4`, кольори як `vec3` або `vec4`, якщо вам потрібна непрозорість. Але, в цілому, обмежень на те як використовувати вектори немає.
Наприклад, якщо ви хочете зберегти лише одне логічне значення в `bvec4`, це можливо, але буде марною витратою пам'яті.
**Примітка**: у шейдері значення кольорів (`R`, `G`, `B`, `A`) нормалізовані, тобто варіюються в діапазоні від 0 до 1, а не від 0 до 0xFF, тому для них краще використовувати тип float `vec4`, ніж цілочисельний тип `ivec4`.
Вже маємо хороший початок, але рушаймо далі!
#### змішування
З вектора можна отримати більше одного значення за раз. Скажімо, із `vec4` вам потрібні лише значення `X` і `Y`. У JavaScript вам довелося б написати щось подібне:
```glsl
var needles = [0, 1]; // розміщення 'x' і 'y' в нашій структурі даних
var a = [0, 1, 2, 3]; // наша структура даних 'vec4'
var b = a.filter(function(val, i, array) {
return needles.indexOf(array.indexOf(val)) != -1;
});
// b = [0, 1]
// або більш буквально:
var needles = [0, 1];
var a = [0, 1, 2, 3]; // структура даних 'vec4'
var b = [a[needles[0]], a[needles[1]]]; // b = [0, 1]
```
Виглядає потворно. У GLSL ви можете отримати ці дані так:
```glsl
// створюємо 4D-вектор типу float
vec4 v4 = vec4(0.0, 1.0, 2.0, 3.0);
// і одночасно отримуємо лише X та Y
vec2 xy = v4.xy; // xy = vec2(0.0, 1.0);
```
Що це щойно сталося?! Коли ви **об'єднуєте аксесори**, GLSL елегантно повертає підмножину значень, які ви запросили, у найбільш відповідному **векторному** форматі.
Тож тут вектор — це структура даних із **довільним доступом**, схожий на масив як у JavaScript.
Таким чином, ви можете не тільки отримати підмножину ваших даних, але і вказати їх **порядок**, у якому вони вам потрібні. Наступний приклад змінює порядок отримання компонентів вектора:
```glsl
// створюємо чотиривимірний вектор типу float: R,G,B,A
vec4 color = vec4(0.2, 0.8, 0.0, 1.0);
// отримуємо компоненти кольору в порядку A,B,G,R
vec4 backwards = color.abgr; // backwards = vec4(1.0, 0.0, 0.8, 0.2);
```
І, звичайно, ви можете повернути той самий компонент кілька разів:
```glsl
// створюємо чотиривимірний вектор типу float: R,G,B,A
vec4 color = vec4(0.2, 0.8, 0.0, 1.0);
// отримуємо vec3 з компонентами GAG на основі каналів G і A з початкового кольору
vec3 GAG = color.gag; // GAG = vec4(0.8, 1.0, 0.8);
```
Це надзвичайно зручно для об'єднання частин вектору, виділення лише rgb-каналів із кольору RGBA тощо.
#### перевантаження
У розділі типів я згадував про **конструктор** і можливість **перевантаження**.
Для тих, хто не знає, **перевантаження** оператора або функції означає _'зміну поведінки зазначеного оператора або функції залежно від операндів/аргументів'_.
В JavaScript немає перевантаження, тому спочатку воно може здатися трохи дивним, але я впевнений, що як тільки ви звикнете до нього, то будете дивуватися, чому це не реалізовано в JS (коротка відповідь - *типізація*).
Найпростіший приклад перевантаженого оператора виглядає так:
```glsl
vec2 a = vec2(1.0, 1.0);
vec2 b = vec2(1.0, 1.0);
// перевантажене додавання
vec2 c = a + b; // c = vec2(2.0, 2.0);
```
ЩО? Отже, можна додавати сутності, які не є числами?!
Саме так. Також це стосується всіх операторів (`+`, `-`, `*` і `/`), але це тільки початок.
Розглянемо наступний фрагмент:
```glsl
vec2 a = vec2(0.0, 0.0);
vec2 b = vec2(1.0, 1.0);
// перевантажений конструктор
vec4 c = vec4(a, b); // c = vec4(0.0, 0.0, 1.0, 1.0);
```
Ми створили `vec4` з двох `vec2`. Таким чином новий `vec4` використав `a.x` і `a.y` як `X`і `Y` компоненти та `b.x` і `b.y` як `Z` і `W` компоненти для вектора `c`.
Ось що відбувається, коли **функція** перевантажується для прийняття різних аргументів, у попередньому випадку це був **конструктор** `vec4`.
Це означає, що в одній програмі може співіснувати багато **версій** одного і того самого методу з різною сигнатурою. Наприклад, усі наступні оголошення є валідними:
```glsl
vec4 a = vec4(1.0, 1.0, 1.0, 1.0);
vec4 a = vec4(1.0); // x, y, z, w - усі дорівнюють 1.0
vec4 a = vec4(v2, float, v4); // vec4(v2.x, v2.y, float, v4.x);
vec4 a = vec4(v3, float); // vec4(v3.x, v3.y, v3.z, float);
тощо
```
Єдине про що ви повинні попіклуватися, так це про передачу достатньої кількості аргументів для заповнення **вектору**.
Нарешті, ви також можете перевантажувати вбудовані функції у вашій програмі, щоб вони могли приймати аргументи, для яких не були розроблені (хоча це не повинно траплятися занадто часто).
#### більше типів
Вектори прикольні і є основою вашого шейдера.
А ще існують інші примітиви, такі як матриці та текстурні семплери, які будуть розглянуті пізніше в книзі.
Ми також можемо використовувати масиви. Звичайно, їх потрібно типізувати й вони мають свої особливості у порівнянні з JS:
* мають фіксований розмір
* ви не можете використовувати методи push(), pop(), splice() тощо, і немає властивості ```length```
* ви не можете відразу ініціалізувати їх значеннями
* Ви повинні встановити значення індивідуально
Це не спрацює:
```glsl
int values[3] = [0, 0, 0];
```
А ось це спрацює:
```glsl
int values[3];
values[0] = 0;
values[1] = 0;
values[2] = 0;
```
Добре, коли ви знаєте свої дані або маєте невеликі масиви значень.
Якщо вам потрібно більше виразності, то можете скористатися типом ```struct```. Це як _об'єкти_ без методів.
Вони дозволяють зберігати та отримувати доступ до кількох змінних всередині одного об'єкта:
```glsl
struct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
```
Ви можете створити та отримати значення _colors_ наступним чином:
```glsl
// ініціалізуємо структуру деякими значеннями
ColorStruct sandy = ColorStruct(
vec3(0.92, 0.83, 0.60),
vec3(1., 0.94, 0.69),
vec3(0.95, 0.86, 0.69)
);
// отримуємо доступ до значень із структури
sandy.color0 // vec3(0.92, 0.83, 0.60)
```
Це синтаксичний цукор, але він може допомогти написати чистіший код, принаймні більш звичний для вас.
#### вирази та умови
Структури даних корисні, але нам оже_ знадобитися можливість для повторення дії або виконання умовних перевірок.
На щастя для нас, синтаксис дуже близький до JavaScript.
Умова виглядає так:
```glsl
if (condition) {
// true
} else {
// false
}
```
Звичайни цикл `for`:
```glsl
const int count = 10;
for (int i = 0; i <= count; i++) {
// do something
}
```
Приклад циклу з ітератором типу float:
```glsl
const float count = 10.;
for (float i = 0.0; i <= count; i += 1.0) {
// do something
}
```
Зауважте, що ```count``` потрібно визначити як ```константу```.
Це означає перед змінною потрібно додати **кваліфікатор** ```const```, який ми розглянемо трохи згодом.
Нам також доступні оператори ```break``` і ```continue```:
```glsl
const float count = 10.;
for (float i = 0.0; i <= count; i += 1.0) {
if (i < 5.) continue;
if (i >= 8.) break;
}
```
Зауважте, що на деяких типах пристроїв ```break``` не працює належним чином і завчасно не перериває виконання циклу.
Загалом, кількість ітерацій має бути якомога меншою, та і в цілому бажано уникати використання циклів і умовних галужень.
#### кваліфікатори
Окрім типів змінних, GLSL використовує **кваліфікатори**.
Коротко кажучи, кваліфікатори допомагають повідомити компілятору призначення змінних.
Наприклад, деякі дані для GPU можуть бути надані тільки від CPU і називаються **атрибутами** та **уніформами**.
**Атрибути** використовуються у вершинних шейдерах, а **уніформи** можна використовувати як у вершинних, так і у фрагментних шейдерах.
Існує також кваліфікатор ```variying```, який використовується для передачі змінних від вершинного шейдеру до фрагментного.
Я не буду вдаватися в деталі, оскільки ми зосереджені на **фрагментних шейдерах**, але далі в книзі ви побачите щось на кшталт:
```glsl
uniform vec2 u_resolution;
```
Бачите, що ми тут зробили? Ми додали кваліфікатор ```uniform``` перед типом змінної.
Це означає, що змінна, яка відповідає за роздільну здатність полотна з яким ми працюємо, передається шейдеру з CPU.
Ширина полотна зберігається в x, а висота в y-компоненті даного 2D-вектора.
Коли компілятор бачить змінну, якій передує цей кваліфікатор, він простежить, щоб ви не могли *змінити* такі значення під час рантайму.
Те саме стосується нашої змінної ```count```, яка слугувала обмеженням для циклу ```for```:
```glsl
const float count = 10.;
for ( ... )
```
Коли ми використовуємо кваліфікатор ```const```, компілятор не дає змогу перезаписувати значення такої змінної, інакше вона не була б константою.
У сигнатурах функцій можуть використовуватися 3 додаткові кваліфікатори: ```in```, ```out``` та ```inout```.
У JavaScript передані до функції значення скалярних аргументів доступні лише для читання. Якщо ви змінюєте їхні значення всередині функції, то ці зміни не застосовуються до змінної поза функцією.
```glsl
function banana(a) {
a += 1;
}
var value = 0;
banana(value);
console.log(value); // 0 - значення за межами функції не змінилося
```
За допомогою кваліфікаторів перед аргументами ви можете вказати їх поведінку:
* ```in``` - лише для читання (за замовчуванням)
* ```out``` - лише для запису: можна змінити, але не можна прочитати значення
* ```inout``` - читання і запис: можна і прочитати й встановити нове значення
Переписаний метод banana у GLSL виглядає так:
```glsl
void banana(inout float a) {
a += 1.;
}
float A = 0.;
banana(A); // тепер A = 1.;
```
Це дуже відрізняється від JS і є досить потужною можливістю, але вам не обов'язково вказувати кваліфікатори аргументів. За замовчуванням вони доступні лише для зчитування.
#### простір і координати
Останнє зауваження: у DOM і Canvas 2D ми звикли, що вісь Y спрямована 'вниз'.
Це має сенс у контексті DOM, оскільки відповідає способу розгортання вебсторінки: панель навігації вгорі, а контент прокручується донизу.
У полотні WebGL вісь Y перевернута і вказує 'вгору'.
Це означає, що початок координат, точка (0, 0), знаходиться в нижньому лівому куті контексту WebGL, а не у верхньому лівому куті, як у 2D Canvas.
Координати текстур також дотримуються цього правила, що спочатку може бути контрінтуїтивним.
## Ось і все!
Звісно, ми б могли більше заглибитися в різноманітні концепції, але, як згадувалося раніше, цей розділ написаний як просте введення для новачків.
Тут вже написано достатньо для того, щоб за деякий час переварити нові знання, але з терпінням і практикою ця мова ставатиме для вас все більш природною.
Сподіваюся, цей матеріал був корисним для вас. А тепер як щодо початку вашої подорожі основною частиною книги?

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": An introduction for those coming from JS";
$README = "README";
$language = "";

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": An introduction for vectors";
$README = "README";
$language = "";

@ -1,6 +1,7 @@
<?php
$path = "../..";
$subtitle = ": An introduction to interpolation";
$README = "README";
$language = "";

@ -0,0 +1,15 @@
# Додаток
1. [Як користуватись книгою офлайн?](00/?lan=ua)
2. [Як запустити приклади на Raspberry Pi?](01/?lan=ua)
3. [Як надрукувати цю книгу?](02/?lan=ua)
4. [Як прийняти участь у розвитку книги?](03/?lan=ua)
5. [Вступ для тих, хто прийшов із JS](04/?lan=ua) від [Nicolas Barradeau](http://www.barradeau.com/)
6. [Введення до векторів](05/?lan=ua)
7. [Введення в інтерполяцію](06/?lan=ua)

@ -1,6 +1,7 @@
<?php
$path = "..";
$subtitle = ": Appendix";
$README = "README";
$language = "";

@ -0,0 +1,5 @@
# Галерея прикладів
<p class="gallery_author">Створено за участі by <a href="https://www.kynd.info">kynd</a>(<a href="https://twitter.com/kyndinfo">@kyndinfo</a>) та Patricio Gonzalez Vivo(<a href="https://twitter.com/patriciogv">@patriciogv</a>)</p>
Це колекція прикладів, взятих із розділів цієї книги разом із шейдерами, люб'язно наданими іншими читачами за допомогою [онлайн-редактора](http://editor.thebookofshaders.com/). Не соромтеся досліджувати та змінювати їх. Якщо у вас вийде приклад, яким ви пишаєтеся, зробіть експорт своїх роботи та надішліть його на адресу [@bookofshaders](https://twitter.com/bookofshaders) або [@kyndinfo](https://twitter.com/kyndinfo). Ми з нетерпінням чекатимемо!

@ -0,0 +1,278 @@
# Глосарій
## За темою
* ТИПИ
[void](./?lan=ua&search=void)
[bool](./?lan=ua&search=bool)
[int](./?lan=ua&search=int)
[float](./?lan=ua&search=float)
[bvec2](./?lan=ua&search=bvec2)
[bvec3](./?lan=ua&search=bvec3)
[bvec4](./?lan=ua&search=bvec4)
[ivec2](./?lan=ua&search=ivec2)
[ivec3](./?lan=ua&search=ivec3)
[ivec4](./?lan=ua&search=ivec4)
[vec2](./?lan=ua&search=vec2)
[vec3](./?lan=ua&search=vec3)
[vec4](./?lan=ua&search=vec4)
[mat2](./?lan=ua&search=mat2)
[mat3](./?lan=ua&search=mat3)
[mat4](./?lan=ua&search=mat4)
[sampler2D](./?lan=ua&search=sampler2D)
[samplerCube](./?lan=ua&search=samplerCube)
[struct](./?lan=ua&search=struct)
* КВАЛІФІКАТОРИ
[attribute](./?lan=ua&search=attribute)
[const](./?lan=ua&search=const)
[uniform](./?lan=ua&search=uniform)
[varying](./?lan=ua&search=varying)
[precision](./?lan=ua&search=precision)
[highp](./?lan=ua&search=highp)
[mediump](./?lan=ua&search=mediump)
[lowp](./?lan=ua&search=lowp)
[in](./?lan=ua&search=in)
[out](./?lan=ua&search=out)
[inout](./?lan=ua&search=inout)
* ВБУДОВАНІ ЗМІННІ
[gl_Position](./?lan=ua&search=gl_Position)
[gl_PointSize](./?lan=ua&search=gl_PointSize)
[gl_PointCoord](./?lan=ua&gl_PointCoord)
[gl_FrontFacing](./?lan=ua&search=gl_FrontFacing)
[gl_FragCoord](./?lan=ua&search=gl_FragCoord)
[gl_FragColor](./?lan=ua&search=gl_FragColor)
* ВБУДОВАНІ КОНСТАНТИ
[gl_MaxVertexAttribs](./?lan=ua&search=gl_MaxVertexAttribs)
[gl_MaxVaryingVectors](./?lan=ua&search=gl_MaxVaryingVectors)
[gl_MaxVertexTextureImageUnits](./?lan=ua&search=gl_MaxVertexTextureImageUnits)
[gl_MaxCombinedTextureImageUnits](./?lan=ua&search=gl_MaxCombinedTextureImageUnits)
[gl_MaxTextureImageUnits](./?lan=ua&search=gl_MaxTextureImageUnits)
[gl_MaxFragmentUniformVectors](./?lan=ua&search=gl_MaxFragmentUniformVectors)
[gl_MaxDrawBuffers](./?lan=ua&search=gl_MaxDrawBuffers)
* ТРИГОНОМЕТРИЧНІ ТА КУТОВІ ФУНКЦІЇ
[radians()](./?lan=ua&search=radians)
[degrees()](./?lan=ua&search=degrees)
[sin()](./?lan=ua&search=sin)
[cos()](./?lan=ua&search=cos)
[tan()](./?lan=ua&search=tan)
[asin()](./?lan=ua&search=asin)
[acos()](./?lan=ua&search=acos)
[atan()](./?lan=ua&search=atan)
* ЕКСПОНЕНЦІАЛЬНІ ФУНКЦІЇ
[pow()](./?lan=ua&search=pow)
[exp()](./?lan=ua&search=exp)
[log()](./?lan=ua&search=log)
[exp2()](./?lan=ua&search=exp2)
[log2()](./?lan=ua&search=log2)
[sqrt()](./?lan=ua&search=sqrt)
[inversesqrt()](./?lan=ua&search=inversesqrt)
* ЗАГАЛЬНІ ФУНКЦІЇ
[abs()](./?lan=ua&search=abs)
[sign()](./?lan=ua&search=sign)
[floor()](./?lan=ua&search=floor)
[ceil()](./?lan=ua&search=ceil)
[fract()](./?lan=ua&search=fract)
[mod()](./?lan=ua&search=mod)
[min()](./?lan=ua&search=min)
[max()](./?lan=ua&search=max)
[clamp()](./?lan=ua&search=clamp)
[mix()](./?lan=ua&search=mix)
[step()](./?lan=ua&search=step)
[smoothstep()](./?lan=ua&search=smoothstep)
* ГЕОМЕТРИЧНІ ФУНКЦІЇ
[length()](./?lan=ua&search=length)
[distance()](./?lan=ua&search=distance)
[dot()](./?lan=ua&search=dot)
[cross()](./?lan=ua&search=cross)
[normalize()](./?lan=ua&search=normalize)
[facefoward()](./?lan=ua&search=facefoward)
[reflect()](./?lan=ua&search=reflect)
[refract()](./?lan=ua&search=refract)
* МАТРИЧНІ ФУНКЦІЇ
[matrixCompMult()](./?lan=ua&search=matrixCompMult)
* ФУНКЦІЇ ПОВ'ЯЗАНІ З ВЕКТОРАМИ
[lessThan()](./?lan=ua&search=lessThan)
[lessThanEqual()](./?lan=ua&search=lessThanEqual)
[greaterThan()](./?lan=ua&search=greaterThan)
[greaterThanEqual()](./?lan=ua&search=greaterThanEqual)
[equal()](./?lan=ua&search=equal)
[notEqual()](./?lan=ua&search=notEqual)
[any()](./?lan=ua&search=any)
[all()](./?lan=ua&search=all)
[not()](./?lan=ua&search=not)
* ФУНКЦІЇ ПОВ'ЯЗАНІ З ТЕКСТУРАМИ
[texture2D()](./?lan=ua&search=texture2D)
[textureCube()](./?lan=ua&search=textureCube)
## За алфавітом
* A
[abs()](./?lan=ua&search=abs)
[acos()](./?lan=ua&search=acos)
[all()](./?lan=ua&search=all)
[any()](./?lan=ua&search=any)
[asin()](./?lan=ua&search=asin)
[atan()](./?lan=ua&search=atan)
[attribute](./?lan=ua&search=attribute)
* B
[bool](./?lan=ua&search=bool)
[bvec2](./?lan=ua&search=bvec2)
[bvec3](./?lan=ua&search=bvec3)
[bvec4](./?lan=ua&search=bvec4)
* C
[ceil()](./?lan=ua&search=ceil)
[clamp()](./?lan=ua&search=clamp)
[const](./?lan=ua&search=const)
[cos()](./?lan=ua&search=cos)
[cross()](./?lan=ua&search=cross)
* D
[degrees()](./?lan=ua&search=degrees)
[dFdx()](./?lan=ua&search=dFdx)
[dFdy()](./?lan=ua&search=dFdy)
[distance()](./?lan=ua&search=distance)
[dot()](./?lan=ua&search=dot)
* E
[equal()](./?lan=ua&search=equal)
[exp()](./?lan=ua&search=exp)
[exp2()](./?lan=ua&search=exp2)
* F
[faceforward()](./?lan=ua&search=faceforward)
[float](./?lan=ua&search=float)
[floor()](./?lan=ua&search=floor)
[fract()](./?lan=ua&search=fract)
* G
[greaterThan()](./?lan=ua&search=greaterThan)
[greaterThanEqual()](./?lan=ua&search=greaterThanEqual)
[gl_FragColor](./?lan=ua&search=gl_FragColor)
[gl_FragCoord](./?lan=ua&search=gl_FragCoord)
[gl_FrontFacing](./?lan=ua&search=gl_FrontFacing)
[gl_PointCoord](./?lan=ua&gl_PointCoord)
[gl_PointSize](./?lan=ua&search=gl_PointSize)
[gl_Position](./?lan=ua&search=gl_Position)
[gl_MaxCombinedTextureImageUnits](./?lan=ua&search=gl_MaxCombinedTextureImageUnits)
[gl_MaxDrawBuffers](./?lan=ua&search=gl_MaxDrawBuffers)
[gl_MaxFragmentUniformVectors](./?lan=ua&search=gl_MaxFragmentUniformVectors)
[gl_MaxVaryingVectors](./?lan=ua&search=gl_MaxVaryingVectors)
[gl_MaxVertexAttribs](./?lan=ua&search=gl_MaxVertexAttribs)
[gl_MaxVertexTextureImageUnits](./?lan=ua&search=gl_MaxVertexTextureImageUnits)
[gl_MaxTextureImageUnits](./?lan=ua&search=gl_MaxTextureImageUnits)
* H
[highp](./?lan=ua&search=highp)
* I
[in](./?lan=ua&search=in)
[inout](./?lan=ua&search=inout)
[int](./?lan=ua&search=int)
[inversesqrt()](./?lan=ua&search=inversesqrt)
[ivec2](./?lan=ua&search=ivec2)
[ivec3](./?lan=ua&search=ivec3)
[ivec4](./?lan=ua&search=ivec4)
* L
[length()](./?lan=ua&search=length)
[lessThan()](./?lan=ua&search=lessThan)
[lessThanEqual()](./?lan=ua&search=lessThanEqual)
[log()](./?lan=ua&search=log)
[log2()](./?lan=ua&search=log2)
[lowp](./?lan=ua&search=lowp)
* M
[matrixCompMult()](./?lan=ua&search=matrixCompMult)
[mat2](./?lan=ua&search=mat2)
[mat3](./?lan=ua&search=mat3)
[mat4](./?lan=ua&search=mat4)
[max()](./?lan=ua&search=max)
[mediump](./?lan=ua&search=mediump)
[min()](./?lan=ua&search=min)
[mix()](./?lan=ua&search=mix)
[mod()](./?lan=ua&search=mod)
* N
[normalize()](./?lan=ua&search=normalize)
[not()](./?lan=ua&search=not)
[notEqual()](./?lan=ua&search=notEqual)
* O
[out](./?lan=ua&search=out)
* P
[precision](./?lan=ua&search=precision)
[pow()](./?lan=ua&search=pow)
* R
[radians()](./?lan=ua&search=radians)
[reflect()](./?lan=ua&search=reflect)
[refract()](./?lan=ua&search=refract)
[return](./?lan=ua&search=return)
* S
[sampler2D](./?lan=ua&search=sampler2D)
[samplerCube](./?lan=ua&search=samplerCube)
[sign()](./?lan=ua&search=sign)
[sin()](./?lan=ua&search=sin)
[smoothstep()](./?lan=ua&search=smoothstep)
[sqrt()](./?lan=ua&search=sqrt)
[step()](./?lan=ua&search=step)
[struct](./?lan=ua&search=struct)
* T
[tan()](./?lan=ua&search=tan)
[texture2D()](./?lan=ua&search=texture2D)
[textureCube()](./?lan=ua&search=textureCube)
* U
[uniform](./?lan=ua&search=uniform)
* V
[varying](./?lan=ua&search=varying)
[vec2](./?lan=ua&search=vec2)
[vec3](./?lan=ua&search=vec3)
[vec4](./?lan=ua&search=vec4)
[void](./?lan=ua&search=void)

@ -0,0 +1,21 @@
## abs
Повертає абсолютне значення параметра.
### Оголошення
```glsl
float abs(float x)
vec2 abs(vec2 x)
vec3 abs(vec3 x)
vec4 abs(vec4 x)
```
### Параметри
**```x```** — значення, яке потрібно повернути в абсолют.
### Опис
**```abs()```** повертає абсолютне значення **`x`**.
<div class="simpleFunction" data="y = abs(x);"></div>
### Дивіться також
[sign()](/glossary/?lan=ua&search=sign), [min()](/glossary/?lan=ua&search=min), [max()](/glossary/?lan=ua&search=max), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,21 @@
## acos
Повертає арккосинус параметра
### Оголошення
```glsl
float acos(float x)
vec2 acos(vec2 x)
vec3 acos(vec3 x)
vec4 acos(vec4 x)
```
### Параметри
**```x```** — значення, арккосинус якого потрібно повернути.
### Опис
**```acos()```** повертає кут, тригонометричний косинус якого дорівнює **`x`**.
<div class="simpleFunction" data="y = acos(x);"></div>
### Дивіться також
[cos()](/glossary/?lan=ua&search=cos), [sin()](/glossary/?lan=ua&search=sin), [asin()](/glossary/?lan=ua&search=asin), [tan()](/glossary/?lan=ua&search=tan), [atan()](/glossary/?lan=ua&search=atan), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,31 @@
## all
Перевіряє чи всі елементи логічного вектора істинні
### Оголошення
```glsl
bool any(bvec2 x)
bool any(bvec3 x)
bool any(bvec4 x)
```
### Параметри
**```x```** — вектор, який буде перевірено на істинність.
### Опис
**```all()```** повертає **`true`**, якщо всі елементи **`x`** мають значення **`true`**, інакше повертається **`false`**. Функціонально це еквівалентно до наступного коду:
```glsl
bool all(bvec x) { // bvec може бути bvec2, bvec3 або bvec4
bool result = true;
int i;
for (i = 0; i < x.length(); ++i) {
result &= x[i];
}
return result;
}
```
### Дивіться також
[any()](/glossary/?lan=ua&search=any), [not()](/glossary/?lan=ua&search=not)

@ -0,0 +1,31 @@
## any
Перевіряє чи хоча б один елемент булевого вектора має значення true
### Оголошення
```glsl
bool all(bvec2 x)
bool all(bvec3 x)
bool all(bvec4 x)
```
### Параметри
**```x```** — вектор, який буде перевірено на істинність.
### Опис
**```any()```** повертає **`true`**, якщо будь-який елемент **`x`** має значення **`true`**, інакше повертається **`false`**. Функціонально це еквівалентно до наступного коду:
```glsl
bool any(bvec x) { // bvec може бути bvec2, bvec3 або bvec4
bool result = false;
int i;
for (i = 0; i < x.length(); ++i) {
result |= x[i];
}
return result;
}
```
### Дивіться також
[any()](/glossary/?lan=ua&search=any), [not()](/glossary/?lan=ua&search=not)

@ -0,0 +1,21 @@
## asin
Повертає арксинус параметра
### Оголошення
```glsl
float asin(float x)
vec2 asin(vec2 x)
vec3 asin(vec3 x)
vec4 asin(vec4 x)
```
### Параметри
**```x```** — значення, арксинус якого потрібно повернути.
### Опис
```asin()``` повертає кут, тригонометричний синус якого дорівнює **`x`**
<div class="simpleFunction" data="y = asin(x);"></div>
### Дивіться також
[cos](/glossary/?lan=ua&search=cos), [sin](/glossary/?lan=ua&search=sin), [acos](/glossary/?lan=ua&search=acos), [tan](/glossary/?lan=ua&search=tan), [atan](/glossary/?lan=ua&search=atan), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,32 @@
## atan
Повертає арктангенс параметрів
### Оголошення
```glsl
float atan(float y, float x)
vec2 atan(vec2 y, vec2 x)
vec3 atan(vec3 y, vec3 x)
vec4 atan(vec4 y, vec4 x)
float atan(float y_over_x)
vec2 atan(vec2 y_over_x)
vec3 atan(vec3 y_over_x)
vec4 atan(vec4 y_over_x)
```
### Параметри
**```y```** — чисельник дробу, арктангенс якого потрібно повернути.
**```x```** — знаменник дробу, арктангенс якого потрібно повернути.
**```y_over_x```** — дріб, арктангенс якого потрібно повернути.
### Опис
**```atan()```** повертає кут, тригонометричний арктангенс якого дорівнює **`y,x`** або **`y_over_x`**, залежно від того, яке саме перевантаження функції викликається.
У першому перевантаженні знаки **`y`** і **`x`** використовуються для визначення квадранта, в якому лежить кут. Значення, які повертає **`atan`** у цьому випадку, знаходяться в діапазоні від -PI до PI. Результати не визначені, якщо **`x`** дорівнює нулю.
Для другого перевантаження **```atan()```** повертає кут, тангенс якого дорівнює **```y_over_x```**. Значення, що повертаються в цьому випадку, знаходяться в діапазоні від -PI до PI.
### Дивіться також
[cos](/glossary/?lan=ua&search=cos), [acos](/glossary/?lan=ua&search=acos), [sin](/glossary/?lan=ua&search=sin), [asin](/glossary/?lan=ua&search=asin), [atan](/glossary/?lan=ua&search=atan), [Розділ 05: Формотворчі функції](/05/?lan=ua), [Розділ 06: Кольори](/06/?lan=ua)

@ -0,0 +1,15 @@
## attribute
Дані атрибутів вершин.
### Приклад
```glsl
attribute vec4 v_color;
```
### Опис
**```attribute```** — змінні доступні лише для читання, що містять дані, які передаються із середовища WebGL/OpenGL у вершинний шейдер.
Оскільки вершинний шейдер виконується один раз для кожної вершини, атрибути визначають дані для кожної вершини, як правило, з такою інформацією, як: положення в просторі, колір, напрямок нормалі та координати текстури вершини.
### Дивіться також
[const](/glossary/?lan=ua&search=const), [uniform](/glossary/?lan=ua&search=uniform), [varying](/glossary/?lan=ua&search=varying), [Розділ 03: Uniforms](/03/?lan=ua)

@ -0,0 +1,15 @@
## bool
Логічний тип даних (також булів, булевий, булівський)
### Приклад
```glsl
bool aBool = true;
bool bBool = bool(aInt);
bool cBool = bool(aFloat);
```
### Опис
**```bool```** — використовується для позначення логічного типу даних. Приймає значення **true** або **false**. Також є одноіменна функція для приведення даних до відповідного типу.
### Дивіться також
[void](/glossary/?lan=ua&search=void), [bool](/glossary/?lan=ua&search=bool), [int](/glossary/?lan=ua&search=int), [float](/glossary/?lan=ua&search=float), [bvec2](/glossary/?lan=ua&search=bvec2), [bvec3](/glossary/?lan=ua&search=bvec3), [bvec4](/glossary/?lan=ua&search=bvec4), [struct](/glossary/?lan=ua&search=struct)

@ -0,0 +1,21 @@
## bvec2
2-вимірний булевий вектор
### Оголошення
```glsl
bvec2 aBvec2 = bvec2(true, true);
bvec2 bBvec2 = bvec2(true);
bvec2 cBvec2 = bvec2(aBvec3);
bvec2 dBvec2 = bvec2(aBvec3.x, aBvec3.y);
```
### Опис
**```bvec2```** — булевий вектор з двома компонентами. Способи ініціалізації:
- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
### Дивіться також
[bool](/glossary/?lan=ua&search=bool), [int](/glossary/?lan=ua&search=int), [float](/glossary/?lan=ua&search=float), [bvec2](/glossary/?lan=ua&search=bvec2), [bvec3](/glossary/?lan=ua&search=bvec3), [bvec4](/glossary/?lan=ua&search=bvec4), [ivec2](/glossary/?lan=ua&search=ivec2), [ivec3](/glossary/?lan=ua&search=ivec3), [ivec4](/glossary/?lan=ua&search=ivec4), [vec2](/glossary/?lan=ua&search=vec2), [vec3](/glossary/?lan=ua&search=vec3), [vec4](/glossary/?lan=ua&search=vec4), [mat2](/glossary/?lan=ua&search=mat2), [mat3](/glossary/?lan=ua&search=mat3), [mat4](/glossary/?lan=ua&search=mat4)

@ -0,0 +1,25 @@
## bvec3
3-вимірний булевий вектор
### Оголошення
```glsl
vec3 aBvec3 = bvec3(true, true, true);
vec3 bBvec3 = bvec3(true);
vec3 cBvec3 = bvec3(aBvec4);
vec3 dBvec3 = bvec3(aBvec4.x, aBvec4.y, aBvec4.z);
vec3 eBvec3 = bvec3(aBvec2, aBool);
vec3 fBvec3 = bvec3(aBvec2.x, aBvec2.y, aBool);
```
### Опис
**```bvec3```** — булевий вектор з трьома компонентами. Способи ініціалізації:
- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання вектора вищої розмірності, де відповідні значення будуть використані для ініціалізації компонентів;
- надання комбінації векторів та/або скалярів. Для ініціалізації вектора використовуються відповідні значення. Аргументи конструктора повинні містити принаймні стільки ж компонентів, скільки ініціалізований вектор.
### Дивіться також
[bool](/glossary/?lan=ua&search=bool), [int](/glossary/?lan=ua&search=int), [float](/glossary/?lan=ua&search=float), [bvec2](/glossary/?lan=ua&search=bvec2), [bvec3](/glossary/?lan=ua&search=bvec3), [bvec4](/glossary/?lan=ua&search=bvec4), [ivec2](/glossary/?lan=ua&search=ivec2), [ivec3](/glossary/?lan=ua&search=ivec3), [ivec4](/glossary/?lan=ua&search=ivec4), [vec2](/glossary/?lan=ua&search=vec2), [vec3](/glossary/?lan=ua&search=vec3), [vec4](/glossary/?lan=ua&search=vec4), [mat2](/glossary/?lan=ua&search=mat2), [mat3](/glossary/?lan=ua&search=mat3), [mat4](/glossary/?lan=ua&search=mat4)

@ -0,0 +1,21 @@
## bvec4
4-вимірний булевий вектор
### Оголошення
```glsl
vec4 aBvec4 = bvec4(true, true, true, true);
vec4 bBvec4 = bvec4(true);
vec4 cBvec4 = bvec4(aBvec2, aBool, aBvec3);
vec4 dBvec4 = bvec4(aBvec2.x, aBvec2.y, aBool, aBvec3.x);
```
### Опис
**```bvec4```** — булевий вектор з чотирма компонентами. Способи ініціалізації:
- надання скалярного значення для кожного компонента;
- надання одного скалярного значення, що буде використано для всіх компонентів;
- надання комбінації векторів та/або скалярів. Для ініціалізації вектора використовуються відповідні значення. Аргументи конструктора повинні містити принаймні стільки ж компонентів, скільки ініціалізований вектор.
### Дивіться також
[bool](/glossary/?lan=ua&search=bool), [int](/glossary/?lan=ua&search=int), [float](/glossary/?lan=ua&search=float), [bvec2](/glossary/?lan=ua&search=bvec2), [bvec3](/glossary/?lan=ua&search=bvec3), [bvec4](/glossary/?lan=ua&search=bvec4), [ivec2](/glossary/?lan=ua&search=ivec2), [ivec3](/glossary/?lan=ua&search=ivec3), [ivec4](/glossary/?lan=ua&search=ivec4), [vec2](/glossary/?lan=ua&search=vec2), [vec3](/glossary/?lan=ua&search=vec3), [vec4](/glossary/?lan=ua&search=vec4), [mat2](/glossary/?lan=ua&search=mat2), [mat3](/glossary/?lan=ua&search=mat3), [mat4](/glossary/?lan=ua&search=mat4)

@ -0,0 +1,21 @@
## ceil
Знаходить і повертає найближче ціле число, яке більше або дорівнює параметру
### Оголошення
```glsl
float ceil(float x)
vec2 ceil(vec2 x)
vec3 ceil(vec3 x)
vec4 ceil(vec4 x)
```
### Параметри
**```x```** — значення для обробки.
### Опис
**```ceil()```** повертає значення, що дорівнює найближчому цілому числу, яке більше або дорівнює **`x`**.
<div class="simpleFunction" data="y = ceil(x);"></div>
### Дивіться також
[floor](/glossary/?lan=ua&search=floor), [fract](/glossary/?lan=ua&search=fract), [mod](/glossary/?lan=ua&search=mod), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,29 @@
## clamp
Повертає значення в діапазоні між двома обмежувачами
### Оголошення
```glsl
float clamp(float x, float minVal, float maxVal)
vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal)
vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal)
vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal)
vec2 clamp(vec2 x, float minVal, float maxVal)
vec3 clamp(vec3 x, float minVal, float maxVal)
vec4 clamp(vec4 x, float minVal, float maxVal)
```
### Параметри
**```x```** — значення для обмеження.
**```minVal```** — нижня межа діапазону.
**```maxVal```** — верхня межа діапазону.
### Опис
**```clamp()```** повертає значення **`x`** обмежене діапазоном від **`minVal`** до **`maxVal`**. Повернене значення обчислюється як **```min(max(x, minVal), maxVal)```**.
<div class="simpleFunction" data="y = clamp(x,0.,1.);"></div>
### Дивіться також
[min](/glossary/?lan=ua&search=min), [abs](/glossary/?lan=ua&search=abs), [max](/glossary/?lan=ua&search=max), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,13 @@
## const
Константний кваліфікатор
### Приклад
```glsl
const float PI = 3.14159265359;
```
### Опис
**```const```** — кваліфікатор, який застосується перед оголошенням будь-якої змінної, щоб вказати, що значення цієї змінної не буде і не може бути змінено.
### Дивіться також
[attribute](/glossary/?lan=ua&search=attribute), [uniform](/glossary/?lan=ua&search=uniform), [varying](/glossary/?lan=ua&search=varying)

@ -0,0 +1,21 @@
## cos
Повертає косинус параметра
### Оголошення
```glsl
float cos(float angle)
vec2 cos(vec2 angle)
vec3 cos(vec3 angle)
vec4 cos(vec4 angle)
```
### Параметри
**```angle```** — величина в радіанах, косинус якої потрібно повернути.
### Опис
**```cos()```** повертає тригонометричний косинус кута.
<div class="simpleFunction" data="y = cos(x);"></div>
### Дивіться також
[acos](/glossary/?lan=ua&search=acos), [sin](/glossary/?lan=ua&search=sin), [asin](/glossary/?lan=ua&search=asin), [tan](/glossary/?lan=ua&search=tan), [atan](/glossary/?lan=ua&search=atan), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,18 @@
## cross
Обчислює перехресний добуток двох векторів
### Оголошення
```glsl
vec3 cross(vec3 x, vec3 y)
```
### Параметри
**```x```** — перший вектор.
**```y```** — другий вектор.
### Опис
**```cross()```** повертає перехресний добуток двох векторів, **`x`** та **`y`**. Вхідними параметрами можуть бути лише 3-компонентні вектори — **`vec3`**. Перехресний добуток еквівалентний добутку довжини векторів на синус (меншого) кута між **`x`** і **`y`**.
### Дивіться також
[dot()](/glossary/?lan=ua&search=dot)

@ -0,0 +1,16 @@
## dFdx
Повертає часткову похідну аргументу відносно x
### Оголошення
```glsl
genType dFdx(float x);
```
### Параметри
**```p```** — вираз, від якого потрібно взяти часткову похідну.
### Опис
Доступний лише у фрагментному шейдері, **```dFdx```** повертає часткову похідну від виразу **`p`** у **`x`**. Похідні обчислюються за допомогою локальної різниці. Вирази, які передбачають похідні вищого порядку, такі як **```dFdx(dFdx(n))```** мають невизначені результати, як і похідні змішаного порядку, такі як **```dFdx(dFdy(n))```**. Передбачається, що вираз **`p`** є безперервним, і тому вирази, обчислені за допомогою нерівномірного потоку керування, можуть бути невизначеними.
### Дивіться також
[dFdy()](/glossary/?lan=ua&search=dFdy)

@ -0,0 +1,16 @@
## dFdy
Повертає часткову похідну аргументу відносно y.
### Оголошення
```glsl
genType dFdy(float y);
```
### Параметри
**```p```** — вираз, від якого потрібно взяти часткову похідну.
### Опис
Ця функція доступна тільки у фрагментному шейдері, **```dFdy```** повертає часткову похідну виразу **```p```** по вісі **```y```**. Похідні обчислюються за допомогою локального диференціювання. Вирази, які передбачають похідні вищих порядків, такі як **```dFdy(dFdy(n))```** мають невизначені результати, так само як і похідні змішаних порядків, наприклад **```dFdy(dFdx(n))```**. Припускається, що вираз **```p```** є неперервним, тому вирази, які обчислюються з використанням нерівномірного потоку керування, можуть бути невизначеними.
### Дивіться також
[dFdx()](/glossary/?lan=ua&search=dFdx)

@ -0,0 +1,21 @@
## degrees
Конвертує передане значення у градуси
### Оголошення
```glsl
float degrees(float radians)
vec2 degrees(vec2 radians)
vec3 degrees(vec3 radians)
vec4 degrees(vec4 radians)
```
### Параметри
**```radians```** — значення у радіанах, яке потрібно перетворити в градуси.
### Опис
**```degrees()```** перетворює значення, передане в радіанах, у градуси.
Обрахунок по формулі: ```(180.0 * radians) / PI```
### Дивіться також
[radians](/glossary/?lan=ua&search=radians)

@ -0,0 +1,23 @@
## distance
Обчислює відстань між двома точками
### Оголошення
```glsl
float distance(float p0, float p1)
float distance(vec2 p0, vec2 p1)
float distance(vec3 p0, vec3 p1)
float distance(vec4 p0, vec4 p1)
```
### Параметри
**```p0```** — перша точка.
**```p1```** — друга точка.
### Опис
**```distance()```** повертає відстань між двома точками **`p0`** і **`p1`**.
<div class="codeAndCanvas" data="../07/circle-making.frag"></div>
### Дивіться також
[length()](/glossary/?lan=ua&search=length), [normalize()](/glossary/?lan=ua&search=normalize), [Розділ 07: Фігури](/07/?lan=ua)

@ -0,0 +1,24 @@
## dot
Обчислює скалярний добуток двох векторів
### Оголошення
```glsl
float dot(float x, float y)
float dot(vec2 x, vec2 y)
float dot(vec3 x, vec3 y)
float dot(vec4 x, vec4 y)
```
### Параметри
**```x```** — перший вектор.
**```y```** — другий вектор.
### Опис
**```dot()```** повертає скалярний добуток двох векторів, **`x`** та **`y`**. Тобто: "**```x[0] * y[0] + x[1] * y[1] +...```**"
Якщо **`x`** і **`y`** однакові, квадратний корінь із скалярного добутку еквівалентний довжині вектора. Вхідними параметрами можуть бути скаляри з типом **`float`** або відповідні вектори. У випадку скалярів функція **```dot()```** є тривіальною та повертає добуток **`x`** та **`y`**.
<div class="codeAndCanvas" data="../07/circle.frag"></div>
### Дивіться також
[cross()](/glossary/?lan=ua&search=cross), [Розділ 07: Фігури](/07/?lan=ua)

@ -0,0 +1,24 @@
## equal
Виконує по-компонентне порівняння двох векторів
### Оголошення
```glsl
bvec2 equal(vec2 x, vec2 y)
bvec3 equal(vec3 x, vec3 y)
bvec4 equal(vec4 x, vec4 y)
bvec2 equal(ivec2 x, ivec2 y)
bvec3 equal(ivec3 x, ivec3 y)
bvec4 equal(ivec4 x, ivec4 y)
```
### Параметри
**```x```** — перший вектор для порівняння.
**```y```** — другий вектор для порівняння.
### Опис
**```equal()```** повертає булів вектор, у якому кожен елемент **`i`** обчислюється як "**```x[i] == y[i]```**".
### Дивіться також
[lessThanEqual()](/glossary/?lan=ua&search=lessThanEqual), [lessThan()](/glossary/?lan=ua&search=lessThan), [greaterThanEqual()](/glossary/?lan=ua&search=greaterThanEqual), [greaterThan()](/glossary/?lan=ua&search=greaterThan), [notEqual()](/glossary/?lan=ua&search=notEqual), [any()](/glossary/?lan=ua&search=any), [all()](/glossary/?lan=ua&search=all), [not()](/glossary/?lan=ua&search=not)

@ -0,0 +1,22 @@
## exp
Повертає експоненту параметра — число Ейлера піднесене до заданого степеня.
Число Ейлера або **`e`** приблизно дорівнює 2.718281828
### Оголошення
```glsl
float exp(float x)
vec2 exp(vec2 x)
vec3 exp(vec3 x)
vec4 exp(vec4 x)
```
### Параметри
**```x```** — степінь до якого потрібно піднести число Ейлера.
### Опис
**```exp()```** повертає число **`e`** піднесене до степеня **`x`**.
<div class="simpleFunction" data="y = exp(x); "></div>
### Дивіться також
[log](/glossary/?lan=ua&search=log), [log2](/glossary/?lan=ua&search=log2), [exp2](/glossary/?lan=ua&search=exp2), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,21 @@
## exp2
Повертає число 2 піднесене до заданого степеня.
### Оголошення
```glsl
float exp2(float x)
vec2 exp2(vec2 x)
vec3 exp2(vec3 x)
vec4 exp2(vec4 x)
```
### Параметри
**```x```** — значення степеня, до якого буде піднесено число 2.
### Опис
**```exp2()```** повертає число 2 у степені **`x`**.
<div class="simpleFunction" data="y = exp2(x); "></div>
### Дивіться також
[log](/glossary/?lan=ua&search=log), [log2](/glossary/?lan=ua&search=log2), [exp](/glossary/?lan=ua&search=exp), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,23 @@
## faceforward
Повертає вектор, що вказує в тому ж напрямку, що й інший
### Оголошення
```glsl
float faceforward(float N, float I, float Nref)
vec2 faceforward(vec2 N, vec2 I, vec2 Nref)
vec3 faceforward(vec3 N, vec3 I, vec3 Nref)
vec4 faceforward(vec4 N, vec4 I, vec4 Nref)
```
### Параметри
**```N```** — вектор для орієнтації.
**```I```** — вектор інциденту.
**```Nref```** — опорний вектор.
### Опис
**```faceforward()```** орієнтує вектор так, щоб він був направлений від поверхні, яка визначена її нормаллю. Якщо **```If dot(Nref, I) < 0```** функція повертає **`N`**, інакше **`-N`**.
### Дивіться також
[reflect()](/glossary/?lan=ua&search=reflect), [refract()](/glossary/?lan=ua&search=refract)

@ -0,0 +1,15 @@
## float
Тип даних для чисел з рухомою крапкою
### Приклад
```glsl
float aFloat = 1.0;
float bFloat = float(aBool);
float cFloat = float(aInt);
```
### Опис
**```float```** — використовується для позначення змінних із числами з рухомою крапкою. Також є одноіменна функція для приведення даних до відповідного типу.
### Дивіться також
[void](/glossary/?lan=ua&search=void), [bool](/glossary/?lan=ua&search=bool), [int](/glossary/?lan=ua&search=int), [float](/glossary/?lan=ua&search=float), [vec2](/glossary/?lan=ua&search=vec2), [vec3](/glossary/?lan=ua&search=vec3), [vec4](/glossary/?lan=ua&search=vec4), [struct](/glossary/?lan=ua&search=struct)

@ -0,0 +1,21 @@
## floor
Знаходить і повертає найближче ціле число, яке менше або дорівнює параметру
### Оголошення
```glsl
float floor(float x)
vec2 floor(vec2 x)
vec3 floor(vec3 x)
vec4 floor(vec4 x)
```
### Параметри
**```x```** — значення для обробки.
### Опис
**```floor()```** повертає значення, що дорівнює найближчому цілому числу, яке менше або дорівнює **`x`**.
<div class="simpleFunction" data="y = floor(x);"></div>
### Дивіться також
[ceil](/glossary/?lan=ua&search=ceil), [fract](/glossary/?lan=ua&search=fract), [mod](/glossary/?lan=ua&search=mod), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,21 @@
## fract
Повертає дробову частину аргументу
### Оголошення
```glsl
float fract(float x)
vec2 fract(vec2 x)
vec3 fract(vec3 x)
vec4 fract(vec4 x)
```
### Параметри
**```x```** — значення для обробки.
### Опис
**```fract()```** повертає дробову частину від **`x`**. Це обчислюється як **```x - floor(x)```**.
<div class="simpleFunction" data="y = fract(x);"></div>
### Дивіться також
[floor](/glossary/?lan=ua&search=floor), [ceil](/glossary/?lan=ua&search=ceil), [mod](/glossary/?lan=ua&search=mod), [Розділ 05: Формотворчі функції](/05/?lan=ua)

@ -0,0 +1,9 @@
## gl_FragColor
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_FragCoord
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -1,4 +1,4 @@
## G_FragCoord
## Gl_FragCoord
### Declaration / Example

@ -0,0 +1,9 @@
## gl_FrontFacing
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxCombinedTextureImages
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxDrawBuffers
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxFragmentUniformVectors
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxTextureImageUnits
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxVaryingVectors
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxVertexAttribs
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_MaxVertexTextureImageUnits
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_PointCoord
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_PointSize
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,9 @@
## gl_Position
### Оголошення / Приклад
### Параметри
### Опис
### Дивіться також

@ -0,0 +1,24 @@
## greaterThan
Виконує по-компонентне порівняння двох векторів на більшість компонентів першого вектора по відношенню до другого
### Оголошення
```glsl
bvec2 greaterThan(vec2 x, vec2 y)
bvec3 greaterThan(vec3 x, vec3 y)
bvec4 greaterThan(vec4 x, vec4 y)
bvec2 greaterThan(ivec2 x, ivec2 y)
bvec3 greaterThan(ivec3 x, ivec3 y)
bvec4 greaterThan(ivec4 x, ivec4 y)
```
### Параметри
**```x```** — перший вектор для порівняння.
**```y```** — другий вектор для порівняння.
### Опис
**```greaterThan()```** повертає булів вектор, у якому кожен елемент **`i`** обчислюється як "**```x[i] > y[i]```**".
### Дивіться також
[lessThanEqual()](/glossary/?lan=ua&search=lessThanEqual), [lessThan()](/glossary/?lan=ua&search=lessThan), [greaterThanEqual()](/glossary/?lan=ua&search=greaterThanEqual), [equal()](/glossary/?lan=ua&search=equal), [notEqual()](/glossary/?lan=ua&search=notEqual), [any()](/glossary/?lan=ua&search=any), [all()](/glossary/?lan=ua&search=all), [not()](/glossary/?lan=ua&search=not)

@ -0,0 +1,24 @@
## greaterThanEqual
Виконує по-компонентне порівняння двох векторів на більшість або рівність компонентів першого вектора по відношенню до другого
### Оголошення
```glsl
bvec2 greaterThanEqual(vec2 x, vec2 y)
bvec3 greaterThanEqual(vec3 x, vec3 y)
bvec4 greaterThanEqual(vec4 x, vec4 y)
bvec2 greaterThanEqual(ivec2 x, ivec2 y)
bvec3 greaterThanEqual(ivec3 x, ivec3 y)
bvec4 greaterThanEqual(ivec4 x, ivec4 y)
```
### Параметри
**```x```** — перший вектор для порівняння.
**```y```** — другий вектор для порівняння.
### Опис
**```greaterThanEqual()```** повертає булів вектор, у якому кожен елемент **`i`** обчислюється як "**```x[i] ≥ y[i]```**".
### Дивіться також
[lessThanEqual()](/glossary/?lan=ua&search=lessThanEqual), [lessThan()](/glossary/?lan=ua&search=lessThan), [greaterThan()](/glossary/?lan=ua&search=greaterThan), [equal()](/glossary/?lan=ua&search=equal), [notEqual()](/glossary/?lan=ua&search=notEqual), [any()](/glossary/?lan=ua&search=any), [all()](/glossary/?lan=ua&search=all), [not()](/glossary/?lan=ua&search=not)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save