Что такое шум Перлина
В 1983 году разработчик Кен Перлин искал способ сделать компьютерную графику менее «пластиковой». Случайные числа давали белый шум — хаотичную рябь без всякой структуры. Ему же нужна была плавная, естественная неровность: такая, какую мы видим в облаках, древесных волокнах или рельефе местности.
Решением стал градиентный шум — функция, которая на каждой точке пространства возвращает не случайное, а согласованное значение: соседние точки получают близкие результаты, а удалённые — почти независимые. Так рождается плавный, органический узор, который можно масштабировать, накладывать слоями и превращать во что угодно — от высоты холма до плотности облака.
Где он используется в играх
Шум Перлина и его производные (симплекс-шум, value noise, worley noise) встречаются почти везде, где нужна «случайная, но правдоподобная» вариативность:
- Ландшафты и высотные карты — значение шума в точке (x, y) становится высотой рельефа, создавая холмы и долины без единого artist-made ассета.
- Облака и атмосфера — несколько слоёв шума разной частоты (октавы) дают клубящуюся, неоднородную текстуру неба.
- Процедурные текстуры — мрамор, дерево, ржавчина и огонь часто рождаются из искажённого шума, а не из фотографий.
- Размещение объектов — распределение деревьев, руды или биомов на карте, чтобы избежать сеточной, «искусственной» регулярности.
Как это работает изнутри
Упрощённо алгоритм для двумерного шума выглядит так: пространство разбивается на сетку, в каждом узле которой лежит случайный вектор-градиент. Для произвольной точки внутри ячейки вычисляется скалярное произведение между вектором до узла и градиентом в этом узле — отдельно для всех четырёх углов ячейки. Затем результаты сглаживаются функцией плавности (fade) и интерполируются — сначала по оси X, затем по оси Y.
// Двумерный шум Перлина в точке (x, y)
function perlin2D(x, y):
x0 = floor(x)
x1 = x0 + 1
y0 = floor(y)
y1 = y0 + 1
// сглаживающая интерполяция, а не линейная
sx = fade(x - x0)
sy = fade(y - y0)
// скалярное произведение градиента и вектора до узла
n00 = dotGridGradient(x0, y0, x, y)
n10 = dotGridGradient(x1, y0, x, y)
ix0 = lerp(n00, n10, sx)
n01 = dotGridGradient(x0, y1, x, y)
n11 = dotGridGradient(x1, y1, x, y)
ix1 = lerp(n01, n11, sx)
return lerp(ix0, ix1, sy) // значение в диапазоне [-1, 1]
На практике редко используют один «слой» шума — обычно накладывают несколько октав с уменьшающейся амплитудой и растущей частотой (так называемый fractal Brownian motion). Это добавляет крупные формы рельефа вместе с мелкими деталями — примерно как в примере на изображении выше, сгенерированном прямо в вашем браузере.
Совет практикующим. Для игр в реальном времени чаще берут симплекс-шум (Simplex noise) — тот же принцип Перлина, но с меньшим числом вычислений на точку и без выраженных артефактов по осям сетки.