The Book of Shaders by Patricio Gonzalez Vivo & Jen Lowe

日本語 - 中文版 - 한국어 - Español - Français - Italiano - Deutsch - Русский - English


Научный коллектив NASA / WMAP

Шум

Время передохнуть! Мы игрались со случайными значениями, которые выглядят как белый шум в расстроенном телевизоре, голова всё ещё кружится от раздумий о шейдерах, а глаза устали. Давайте выберемся на прогулку!

Мы ощущаем ветер кожей, солнце светит в лицо. Мир - очень яркое и богатое место. Цвета, фактуры, звуки. На прогулке мы не можем не заметить поверхности дорог, камней, деревьев и облаков.

Непредсказуемость этих текстур можно было бы назвать случайной, но они не выглядят как та беспорядночность, с которой мы играли ранее. «Реальный мир» - это настолько богатое и сложное место! Как аппроксимировать это разнообразие с помощью вычислений?

Этот вопрос Кен Перлин пытался разрешить в начале 1980-ых, когда он занимался генерацией реалистичных текстур для фильма «Трон». В итоге, он предложил элегантный оскароносный алгоритм генерации шума.

Дисней - Трон (1982)

Код ниже не реализует классический алгоритм шума Перлина, но это хорошее начало в изучении способов генерации шума.

В этих строках мы делаем что-то похожее на код из предыдущей главы. Мы разбиваем непрерывное значение с плавающей точкой (x) на целую (i) и дробную (f) части. Для получения целой части мы используем floor(), а для дробной - fract(). Затем мы применяем rand() к целой части x, получая уникальное случайное число для каждого нового целого.

Далее идут две закомментированные строки. Первая из них линейно интерполирует каждое случайное значение.

y = mix(rand(i), rand(i + 1.0), f);

Раскомментируйте строку и посмотрите что получится. Мы используем значение fract() из переменной f для смешивания (mix()) двух случайных значений.

Ранее мы уже видели лучшее решение, чем линейная интерполяция, не так ли? Попробуйте раскомментировать следующую строку, в которой используется гладкая интерполяция (smoothstep()) вместо линейной:

y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));

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

float u = f * f * (3.0 - 2.0 * f ); // самописная кубическая кривая
y = mix(rand(i), rand(i + 1.0), u); // её использование в интерполяции

Эта плавная беспорядочность существенно меняет расклад. Она помогает графическим инженерам и художникам генерировать изображения и геометрию с ощущением целостности. Алгоритм шума Перлина был многократно реализован на разных языках и для различных размерностей, помогая создавать завораживающие произведения искусства в самых разных видах творческой деятельности.

Роберт Ходжин - Написанные изображения (2010)

Теперь ваш ход:

Двумерный шум

Теперь мы знаем как сделать шум в 1D, а значит самое время двигаться в 2D. В двумерном пространстве вместо интерполяции между двумя точками на прямой (fract(x) и fract(x)+1.0), мы будем интерполировать между четырьмя углами квадратного участка плоскости (fract(st), fract(st)+vec2(1.,0.), fract(st)+vec2(0.,1.) и fract(st)+vec2(1.,1.)).

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

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

Посмотрите на следующую функцию шума.

Мы начинаем с масштабирования пространства в 5 раз (строка 45), чтобы увидеть интерполяцию между клетками решётки. Далее в функции шума мы разделяем пространство на клетки. Дробную часть координаты пикселя мы сохраняем для использования в качестве нормализованной координаты внутри клетки, а целую часть - как координату самой клетки. Целочисленная координата используется для вычисления четырёх координат четырёх углов и получения случайного значения для каждого из них (строки 23-26). Наконец, в строке 35 мы интерполируем между четырьмя случайными значениями в углах, используя ранее сохранённые дробные части координат.

Теперь ваш ход. Выполните следующие упражнения:

Марк Ротко - Три (1950)

Использование шума в генеративном дизайне

Изначально алгоритмы шума разрабатывались чтобы придать естественное что-то цифровым текстурам. Одно- и двумерные реализаци, которые мы рассмотрели выше, работали на интерполяции шума значений, из-за чего мы называем их шумом значений. Но существуют и другие способы получения шума...

Иниго Квилес - шум значений

Как мы выяснили в предыдущих упражнениях, шум значений выглядит «блочно». Чтобы справиться с этим блочным эффектом, Кен Перлин в 1985 году разработал другую реализацию алгоритма под названием градиентный шум. Кен додумался как интерполировать случайные градиенты вместо значений. Эти градиенты возвращаются двумерной случайной функцией, которая возвращает направления (в виде vec2) вместо скалярных значений. Щёлкните по следующему изображению чтобы увидеть код и результат его работы.

Иниго Квилес - градиентный шум

Посмотрите на эти два примера шума, написанные Иниго Квилесом, и обратите внимание на разницу между шумом значений и градиентным шумом.

Как и художники, понимающие принцип работы пигментов своих красок, чем больше мы знаем о реализации шумовых алгоритмов - тем лучше мы сможем их использовать. Например, если повернуть двумерное изображение прямых линий на значение двумерного шума, получится следующий эффект закрутки, имитирующий древесину. Кликните на изображение, чтобы увидеть код.

Текстура древесины

    pos = rotate2d( noise(pos) ) * pos; // поворачиваем пространство
    pattern = lines(pos,.5); // рисуем линии

Другой способ получить интересные картинки - это представить шум в виде поля расстояний и применить какие-нибудь трюки из главы о фигурах.

Текстура поверхности воды

    color += smoothstep(.15,.2,noise(st*10.)); // Чёрные всплески
    color -= smoothstep(.35,.4,noise(st*10.)); // Отверстия во всплесках

Ещё один способ использования шума - модуляция форм. Здесь так же потребуются навыки из главы о фигурах.

Практические задания:

Джексон Поллок - Серый №14 (1948)

Улучшенный шум

Перлин улучшил свой непростой шум, предложив симплексный шум, в котором заменил кубическую эрмитову кривую ( f(x) = 3x^2-2x^3 , которая идентична функции smoothstep()) кривой пятой степени ( f(x) = 6x^5-15x^4+10x^3 ). При этом оба конца кривой становятся более «плоскими», поэтому границы более плавно сшиваются друг с другом. Другими словами, получается более непрерывный переход между клетками. Посмотрите на результат, раскомментировав вторую формулу в следующем графике (или два уравнения по отдельности здесь).

Обратите внимание как меняются концы кривой. Подробнее читайте в оригинальной статье.

Симплексный шум

Перлин не удовлетворился успехом своего алгоритма. Он хотел более высокой производительности. На Siggraph 2001 он представил «симплексный шум», который вносит следующие улучшения относительно предыдущего алгоритма:

Я знаю о чём вы думаете... «Кто этот человек?» Да, он делает фантастические вещи! Но серьёзно, как он улучшил алгоритм? Смотрите: если для двух измерений мы интерполируем 4 точки (углы квадрата), то для трёх (пример реализации здесь) и четырёх измерений придётся интерполировать 8 и 16 точек. Так? Другими словами, для N измерений нужно гладко интерполировать 2 в степени N точек. Но Кен вспомнил, что хоть квадрат и является удобной формой для заполнения пространства, но всё же простейшая фигура в двумерном пространстве - это равносторонний треугольник. Поэтому он начал с замены квадратной решётки (которую мы использовали ранее) на симплексную решётку, то есть равносторонние треугольники в двумерном случае.

Симплекс в N измерениях - это многогранник с N + 1 углом. Другими словами, в двумерном пространстве придётся вычислять на один угол меньше, в трёхмерном - на 4 угла меньше, а в четырёхмерном - на 11 углов меньше! Это огромное достижение!

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

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

Далее, следуем описанию из статьи Стефана Густавсона: «...рассмотрев целые части трансформированных координат (x,y) в данной точке, нетрудно определить к какой клетке, составленной из двух симплексов, принадлежит эта точка. А сравнив абсолютные значения x и y, мы можем определить принадлежность точки к верхнему или нижнему симплексу, после чего останется обойти три корректных угловых точки.»

В следующем коде раскомментируйте строку 44 чтобы увидеть перекос решётки, а затем раскомментируйте строку 47 чтобы увидеть как строится решётка из симплексов. В строке 22 обратите внимание как мы разделяем скошенный квадрат на два равносторонних треугольника, всего лишь проверяя условия x > y («нижний» треугольник) или y > x («верхний» треугольник).

Все эти улучшения порождают произведение алгоритмического искусства под названием Симплексный шум. Ниже приведена реализация этого алгоритма, написанная на GLSL Йаном МакЭваном и Стефаном Густавсоном (и представленная в этой работе). Она излишне сложна для образовательных целей, но всё же не поленитесь кликнуть на картинку и изучить исходный код. Он быстр, лаконичен и не настолько запутан, как вы возможно его себе представляли.

Йан МакЭван из Ashima Arts - Симплексный шум

Ну что-ж... пора заканчивать с техническими деталами, пора задействовать этот ресурс в качестве вашего нового средства выразительности:

В этой главе мы научились немного контролировать хаос. Это было непросто! Нужно потрудиться и набраться терпения, чтобы стать мастером по работе с шумом.

В следующих главах мы изучим некоторые известные приёмы чтобы отточить ваши умения и научиться делать из шума более качественную генеративную графику с помощью шейдеров. А пока немного отдохните на свежем воздухе, рассматривая причудливые узоры природы. Тренировка умения наблюдать требует столько же или даже больше сил, чем оттачивание умения создавать. Пойдите на улицу и хорошо отдохните до конца дня!

«Поговорите с деревом, подружитесь с ним.» Боб Росс