The Book of Shaders by Patricio Gonzalez Vivo & Jen Lowe

Bahasa Indonesia - Tiếng Việt - 日本語 - 中文版 - 한국어 - Español - Portugues - Français - Italiano - Deutsch - Русский - Polski - English


Узоры

Шейдерные программы исполняются попиксельно, поэтому вне зависимости от количества повторений фигуры объём вычислений не изменяется. Это значит, что фрагментные шейдеры хорошо справляются с повторяющимися узорами.

Нина Вормердам - Проект IMPRINT (2013)

В этой главе мы собираемся применить весь ранее изученный материал, и повторить это в изображении несколько раз. Как и в предыдущих главах, наша стратегия будет основана на умножении пространственных координат (между 0.0 и 1.0), так чтобы фигуры, которые мы рисуем между 0.0 и 1.0 повторялись несколько раз, образуя решётку.

«Регулярная решётка - это то, с чем человеческой интуиции и изобретательности проще всего работать. Повторяющиеся элементы вступают в контраст с хаосом мироздания и создают ощущение порядка. Люди всегда старались украсить и разнообразить окружающее пространство с помощю повторяющихся элементов. Это прослеживается от доисторических узоров на керамике до геометрических мозаик римских бань.» 10 PRINT, Издательство MIT, (2013)

Для начала давайте вспомним функцию fract(). Она возвращает дробную часть числа, то есть работает как взятие остатка от деления на единицу (mod(x,1.0)). Другими словами, fract() возвращает число справа от точки. Переменная с нормализованными координатами (st) уже пробегает значения от 0.0 до 1.0, поэтому нет смысла делать что-то вроде:

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);
}

Но если мы увеличим масштаб нормализованной системы координат, скажем, в три раза, то получится три отрезка линейной интерполяции между 0.0 и 1.0: между 0 и 1, между 1 и 2, и наконец между 2 и 3.

Теперь давайте нарисуем что-нибудь в каждом подпространстве, раскомментировав строку 27. Соотношение сторон при этом не изменится и фигуры не будут искажены, ибо мы умножаем по обеим осям.

Для более полного понимания попробуйте выполнить следующее:

Применение матриц к узорам

Каждая клетка решётки является уменьшенной версией нормализованной системы координат, которую мы использовали ранее, поэтому мы можем применить к этим клеткам матричные преобразования переноса, поворота и масштаба.

Векторный шотландский узор от Kavalenkava

Узоры со сдвигом

Давайте сымитируем кирпичную стену. Каждый ряд кирпичей в стене смещён на полкирпича по оси x относительно предыдущего ряда. Как это сделать?

Для начала нам нужно узнать чётность номера строки, над которой работает данный поток. Таким образом мы сможем понять, нужно ли делать сдвиг по x в этой строке.

Чтобы определить чётность строки, используем взятие по модулю 2.0 с помощью функции mod(), и сравним результат с единицей. Посмотрите на формулы ниже и раскомментируйте две последние строки.

Как видите, мы могли бы использовать тернарный оператор для сравнения значения по модулю 2.0 с единицей, но того же эффекта можно достичь с помощью step(), которая работает быстрее. Почему? Хотя мы и не знаем как графические карты оптимизирует код, безопаснее будет предположить что встроенные функции работает быстрее, чем не встроенные. Если у вас есть возможность использовать встроенную функцию - используйте!

Теперь у нас есть формула вычисления чётности и мы можем сдвинуть нечётные строки для создания эффекта кирпичной стены. В 14 строке следующего кода мы используем эту формулу для «обнаружения» нечётных строк. Как видите, для чётных строк функция возвращает 0.0, что при умножении на сдвиг 0.5 так же даёт 0.0, а значит чётные строки не сдвигаются. В нечётных же строках 0.5 умножается на 1.0, поэтому в них пространство сдвигается на 0.5 по оси x.

Теперь попробуйте раскомментировать строку 32. Это сделает соотношение сторон похожим на соотношение сторон кирпича. А раскомментировав 40 строку вы увидите отображение координат в красный и зелёный цвета.

Плитка Труше

Теперь, когда мы научились определять чётность строки и столбца для каждой клетки, мы можем многократно использовать один и тот же элемент в зависимости от его расположения. Рассмотрим плитку Труше, где один элемент дизайна может быть представлен четырьмя различными способами:

Изменяя рисунок в зависимости от расположения плитки, можно создать бесконечно много сложных изображений.

Обратите особое внимание на функцию rotateTilePattern(), которая разделяет пространство на четыре части и задаёт угол поворота каждой из них.

Создавайте свои собственные правила

Создание процедурных узоров - это умственное упражнение по поиску минимальных повторно используемых элементов. Это древняя практика. Мы, как биологический вид, издавна использовали узоры для украшения тканей, пола и границ объектов: вспомните извилистые узоры древней Греции или решётчатые узоры из Китая. Магия повторений и вариаций всегда захватывала наше воображение. Рассмотрите декоративные узоры и обратите внимание как художники и дизайнеры балансируют между предсказуемостью порядка и внезапностью изменчивости хаоса. От арабских геометрических узоров и до бесподобных африканских тканей раскинулась целая вселенная узоров, среди которых есть что изучить.

Франц Сейлс Мейер - Учебник орнамента (1920)

Этой главой мы заканчиваем раздел об алгоритмическом рисовании. В следующих главах мы привнесём в шейдеры немного энтропии для создания генеративного дизайна.