|
|
|
|
# Обробка зображень
|
|
|
|
|
|
|
|
|
|
## Текстури
|
|
|
|
|
|
|
|
|
|
![](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)
|
|
|
|
|
|
|
|
|
|
* Які ще оптичні іграшки можна відтворити за допомогою текстур?
|
|
|
|
|
|
|
|
|
|
У наступних розділах ми побачимо як за допомогою шейдерів можна обробляти зображення, виконуючи певні операції. Зрештою шейдери не в останню чергу розроблені саме для цього.
|