Процедурная генерация: шум, L-системы, данжены
Механизм: гладкая случайность и вшитые ограничения
Шум Перлина/Simplex
Обычный random() даёт «снег» — соседние значения независимы. Для рельефа нужна гладкая случайность: близкие точки — близкие высоты. Шум Перлина: в узлах решётки — случайные градиент-векторы; для точки берём скалярные произведения градиентов на векторы-расстояния до узлов и гладко интерполируем → значение в . Одна октава — плавные холмы; детали дают октавы (fBm) — сумма копий шума растущей частоты и падающей амплитуды:
( — lacunarity, множитель частоты; — persistence, множитель амплитуды, обычно , ). Крупные октавы задают горы, мелкие — камни и рябь: тот самый фрактальный рельеф Minecraft. Быстро (O(1) на сэмпл, прекомпутируемо), параметризуемо, понятно. Simplex (Перлин, 2001) быстрее и без направленных артефактов; его 3D+-патент истёк 8 янв 2022, а OpenSimplex (Kurt Spencer, 2014) — clean-room патент-фри альтернатива.
L-системы, BSP, грамматики
- L-системы — строковые правила переписывания: аксиома
F, правилоF → F[+F]F[-F]F, итерируешь, интерпретируешь как «черепашью графику» (F— вперёд,[]— стек позиции,±— поворот). Рекурсия в правилах даёт древовидное ветвление — процедурная растительность. - BSP (Binary Space Partition) — рекурсивно дели прямоугольник, комнаты в листьях, коридоры между соседями. Гарантирует связность (коридор существует), естественная иерархия, O(n). Данжены Rogue/Diablo/рогаликов.
- Грамматики уровней —
Level → Entrance Room+ Boss Exit: сэмплируешь из грамматики + проверяешь ограничения (выход достижим, сложность сбалансирована).
Главный инсайт: ограничения вшиты в генерацию (Spelunky)
Spelunky (2008) шипнулся с процедурными уровнями, которые ощущаются рукотворными. Секрет — constraint satisfaction на этапе генерации, а не после: генератор гарантирует путь к выходу, рост сложности с глубиной, отсутствие тупиков — потому что ограничения вшиты в процесс, а не проверяются постфактум. «Сгенерируй уровень, потом проверь валидность» проигрывает «генерируй так, чтобы валидность не могла нарушиться». Отсюда определение: PCG — это не «случайно», это ограниченная случайность, где ограничения обеспечивают качество.
А GAN'ы? Фронтир, который обычно проигрывает
ML-augmented PCG существует: обучи GAN на уровнях (Mario-level papers, Volz et al.) — он выучит распределение «хороших» уровней и умеет интерполировать. Но на практике для шипабельного контента он проигрывает классике: выход часто невалиден (несостыкованные тайлы, непроходимо), трудно наложить жёсткие ограничения (уровень обязан быть проходим — а GAN гарантий не даёт), и он медленнее. ML-PCG полезен точечно (блендинг стилей, аугментация, текстуры), но структурный, ограничениями-связанный контент почти всегда дешевле и надёжнее сгенерировать классикой — это та же история, что с RL-агентами, но про контент.
🕹 Во что поиграть — и что заметить
Мир Minecraft — это шум Перлина/Simplex по высоте (+пещеры 3D-шумом); NMS генерит планеты тем же аппаратом. Крупные формы + мелкие детали = октавы fBm.
🎮 Заметь: в Minecraft взлети повыше и разгляди иерархию масштабов: большие холмы (низкая октава) с наложенными буграми и рябью (высокие октавы). Это буквально сумма октав из формулы. И весь мир — не хранится, а вычисляется из seed'а (храни функцию, а не выход — как в железе).
Каждый уровень уникален и при этом всегда проходим: путь к выходу гарантирован генерацией, сложность растёт с глубиной. Ощущается рукотворным, хотя процедурен.
🎮 Заметь: в Spelunky сыграй несколько уровней и убедись — выход всегда достижим, тупиков-ловушек нет. Это не везение генератора, а вшитое ограничение. Сравни с ощущением «рандомного» уровня без ограничений (был бы непроходимый бардак). Вот разница «ограниченная случайность» vs «просто random».
Классические рогалики (и Diablo) строят подземелья BSP: рекурсивное деление → комнаты в листьях → коридоры между соседями. Всегда связно, дёшево, «выглядит хорошо».
🎮 Заметь: в рогалике посмотри на карту данжена: комнаты сгруппированы иерархически, соединены коридорами, нет изолированных областей. Это отпечаток BSP-дерева. Простой детерминированный алгоритм даёт связный играбельный уровень без всякого обучения.
Хардкор · value vs gradient noise, октавы, Simplex/OpenSimplexможно пропустить
Value noise vs gradient noise
Value noise: случайные значения в узлах решётки + гладкая интерполяция (smoothstep). Просто, но даёт видимую «блочность» решётки. Gradient noise (Перлин): случайные градиенты в узлах, значение = интерполяция скалярных произведений градиент·(точка−узел); в узлах ноль, между — плавные экстремумы → визуально богаче, меньше осевых артефактов. fBm поверх любого: — октавы добавляют детали на убывающих масштабах; при ряд сходится, спектр степенной (потому и «фрактальный»/природный). Вариации: ridged (|noise|, гребни-хребты), domain warping (шум от координат, искажённых шумом — реки/прожилки).
Simplex, патент и OpenSimplex
Классический Перлин на кубической решётке требует углов в D (в 3D — 8, в 4D — 16) и даёт направленные артефакты по осям. Simplex (Перлин, 2001) кладёт узлы в симплекс-решётку → углов (в 3D — 4), меньше умножений, лучше масштабируется в высокие размерности, нет осевых артефактов. Загвоздка: 3D+-использование Simplex было запатентовано (US 6,867,776), поэтому многие движки годами обходились Перлином; патент истёк 8 янв 2022. OpenSimplex (Kurt Spencer, 2014) — clean-room свободная альтернатива, тоже gradient noise, без артефактов Перлина. Практика: бери Simplex/OpenSimplex для скорости и изотропности, Перлин — если и так хватает.
Хардкор · почему классика бьёт GAN и связь с constrained-декодингомможно пропустить
Валидность как жёсткое ограничение
Игровой контент почти всегда имеет жёсткие инварианты: уровень обязан быть проходим, тайлы — стыковаться, данжен — связен. Классический PCG вшивает их в генерацию (BSP гарантирует связность конструктивно; Spelunky — путь к выходу; WFC — совместимость соседей). GAN учит мягкое распределение «похоже на хорошие уровни» и легко нарушает жёсткие инварианты — а постфактум-проверка+ресэмпл дорога и не гарантирует сходимости. Плюс контроль: у шума/грамматики есть ручки (частота, правила, seed), у GAN — латент, который трудно связать с «сделай проход шире». Поэтому для структурного контента ручной генератор+ограничения побеждает; ML заходит там, где распределение слишком богато для ручных правил (текстуры, натуральные изображения, диалоги), а жёстких инвариантов мало.
Тот же приём в LLM: constrained decoding
«Генерируй-с-ограничениями > сгенерируй-потом-проверь» — дословно structured/constrained generation в LLM: grammar-constrained decoding, JSON-schema/regex-принуждение, маскирование недопустимых токенов на каждом шаге гарантируют валидный вывод конструктивно, вместо «сгенерируй свободно → распарсь → отвергни → повтори». Это ровно инсайт Spelunky, перенесённый на генерацию токенов: вшей инвариант в процесс. А fBm/октавы ↔ роль структурных приоров и мультимасштабных/частотных разложений в ML (Fourier features, позиционные кодировки, multi-resolution). Мета-урок общий: дешёвый алгоритмический генератор с ограничениями часто бьёт дорогой обученный, когда в домене есть жёсткие правила валидности.
ML / AI (твой домен): PCG-vs-GAN — контентная сторона всего тезиса: для структурной генерации с жёсткими инвариантами ручные правила + constraint satisfaction бьют обученный генератор (валидность, контроль, стоимость). Прямой мост — constrained/structured decoding в LLM: grammar/JSON-schema/regex-принуждение и маскирование токенов гарантируют валидный вывод конструктивно — ровно «генерируй-с-ограничениями > сгенерируй-потом-проверь-и-ретрай» (инсайт Spelunky на уровне токенов). Общий паттерн — генеративная модель + жёсткие ограничения (диффузия с guidance, program synthesis с типами, constrained sampling). fBm/октавы ↔ структурные приоры и мультимасштабные/частотные разложения (Fourier features, позиционные кодировки). «Храни функцию, а не выход» (мир из seed'а) ↔ неявные представления/INR и компрессия как генерация. А мета-урок — алгоритмический генератор с ограничениями часто лучше обученного, когда домен структурен, — та же дисциплина «когда ML не ответ», что и в RL-агентах, только про генерацию.
Дизайн/контент: ограниченная случайность = контролируемая вариативность; ручки (частота, правила, seed) важнее «магии» — воспроизводимость и правки.
Системы: «храни функцию, а не выход» — генерация на лету экономит память/полосу (мир из seed'а), детерминированная воспроизводимость по seed.
Принцип: для структурного контента бери алгоритм + ограничения по умолчанию; ML — где распределение слишком богато для правил и мало жёстких инвариантов; вшивай валидность в процесс, а не проверяй постфактум.
Перлин, Simplex, OpenSimplex — что брать?
Почему PCG называют «не случайной»?
random() для контента даёт мусор: несвязный рельеф, непроходимые уровни, деревья-кляксы. Работающая PCG — это ограниченная случайность: случайность есть, но она течёт по руслам, заданным структурой и ограничениями. Гладкость шума (близкие точки → близкие высоты) — это ограничение на «случайность» рельефа. Связность BSP (коридор между соседями всегда есть) — жёсткий инвариант. Проходимость Spelunky — вшитое правило. Совместимость соседей в WFC — ограничение. Именно ограничения превращают «шум» в «мир, который ощущается сделанным». Отсюда практический сдвиг мышления: не «сгенерирую случайно и посмотрю», а «какие инварианты обязаны держаться, и как встроить их в генерацию, чтобы они не могли нарушиться». Случайность даёт вариативность; ограничения дают качество; PCG — их произведение.GAN'ы реально проигрывают классике для уровней?
Как «генерируй-с-ограничениями» переносится на LLM?
- Ken Perlin — «Improving Noise» (2002, Simplex); Stefan Gustavson — «Simplex noise demystified».
- «The Book of Shaders» (гл. про noise) + Red Blob Games — интерактивные объяснения шума/fBm.
- Przemyslaw Prusinkiewicz — «The Algorithmic Beauty of Plants» (L-системы).
- Derek Yu — постмортем генерации Spelunky (constrained randomness); Volz et al. — «Evolving Mario Levels in the Latent Space of a GAN» (2018).
- Модуль 11, §4 «Procedural Content Generation» (
11-ai-ml-in-games.md).