О чем этот пример
Визуализация процедурного шума — мощный инструмент для создания уникальных текстур, ландшафтов и атмосферы в играх. Этот пример демонстрирует, как с помощью всего нескольких строк кода в Phaser 3 сгенерировать не просто поле шума, а полноценную карту нормалей, которая сразу создаёт иллюзию объёма и рельефа. Мы разберём, как это работает и зачем может пригодиться в ваших проектах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create()
{
const { width, height } = this.scale;
this.noise1 = this.add.noisesimplex2d({
noiseCells: [ 3, 4 ],
noiseIterations: 4,
noiseValueFactor: 0.4 // Eliminates some regions where octaves add > 1.
}, width * 1 / 4, height / 2, width / 2, height);
this.noise2 = this.add.noisesimplex2d({
noiseCells: [ 3, 4 ],
noiseIterations: 4,
noiseValueFactor: 0.4,
noiseNormalMap: true,
noiseNormalScale: 8
}, width * 3 / 4, height / 2, width / 2, height);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Суть примера: две визуализации одного шума
В примере создаются два объекта NoiseSimplex2D с идентичными параметрами шума, но разными режимами отображения. Они располагаются рядом на сцене для наглядного сравнения.
Первый объект (noise1) отображает сырые значения шума в градациях серого — это классическая карта высот. Второй объект (noise2) использует эти же значения для генерации карты нормалей, которая имитирует освещение рельефа, созданного этим шумом.
Ключевое отличие — всего один параметр в конфигурации второго объекта.
Разбираем конфигурацию шума: `noiseSimplex2d`
Метод this.add.noisesimplex2d() создаёт и добавляет на сцену 2D-текстуру, заполненную значениями Simplex-шума. Его первый аргумент — объект конфигурации.
{
noiseCells: [ 3, 4 ],
noiseIterations: 4,
noiseValueFactor: 0.4,
noiseNormalMap: true,
noiseNormalScale: 8
}
- noiseCells: [3, 4] — Определяет, сколько "ячеек" или паттернов шума будет уложено по ширине (3) и высоте (4) текстуры. Меньшие значения дают более крупные, плавные структуры.
- noiseIterations: 4 — Количество октав шума. Каждая следующая октава имеет бóльшую частоту и меньшую амплитуду, что добавляет детализацию. 4 итерации — хороший баланс между плавностью и сложностью.
- noiseValueFactor: 0.4 — Множитель для итогового значения, который "прижимает" его к диапазону [0, 1]. Без этого октавы могут сложиться в значение больше 1, что приведёт к "пересветам" на текстуре.
- noiseNormalMap: true — Флаг, который включает расчёт и применение карты нормалей к визуализации. Именно он превращает серую карту высот в иллюзию 3D-рельефа.
- noiseNormalScale: 8 — Коэффициент, влияющий на "крутизну" воспринимаемого рельефа. Чем выше значение, тем более контрастными и резкими выглядят "склоны".
Геометрия размещения на сцене
Второй и последующие аргументы метода задают позицию и размер отображаемого объекта на Canvas.
this.add.noisesimplex2d(config, x, y, width, height)
В примере координаты и размеры рассчитываются динамически относительно размеров окна игры (this.scale.width и this.scale.height).
// Первый шум (обычный) занимает левую половину экрана по ширине
this.noise1 = this.add.noisesimplex2d(config1, width * 1 / 4, height / 2, width / 2, height);
// Второй шум (с нормалями) занимает правую половину
this.noise2 = this.add.noisesimplex2d(config2, width * 3 / 4, height / 2, width / 2, height);
- `xиy: Это координаты центра объекта. Размещая первый объект наwidth * 1 / 4, а второй наwidth * 3 / 4`, мы центрируем каждый в своей половине экрана.
- width и height: Фактический размер текстуры на экране. Оба объекта растягиваются на половину ширины и полную высоту экрана, создавая два больших, сопоставимых прямоугольника.
Практическое применение в играх
Карты нормалей, сгенерированные из шума, — это не просто красивая визуализация. Их можно использовать напрямую в качестве текстур для придания поверхности сложного, не повторяющегося рельефа без увеличения полигонов.
1. **Динамический рельеф местности:** Создайте базовую плоскость (плитку) для травы, камня или льда и наложите на неё такую текстуру нормалей. Это мгновенно "оживит" плоскую поверхность, создав иллюзию неровностей, камней или волн.
2. **Процедурные небеса и облака:** Используйте шум с малым значением noiseNormalScale для создания лёгкой, турбулентной объёмности в текстурах облаков или туманности.
3. **Генерация карт для шейдеров:** Полученную текстуру можно передать в пользовательский шейдер как uniform для создания более сложных эффектов, например, динамического освещения лавой или воды на procedurally generated поверхности.
Сила подхода в том, что вы получаете визуально богатый результат, управляя всего несколькими числовыми параметрами, и можете изменять его в реальном времени.
Что попробовать дальше
Пример наглядно показывает, как Phaser 3 из коробки предоставляет мощные инструменты для работы с процедурной генерацией. Включив noiseNormalMap: true, вы превращаете абстрактную карту высот в готовый к использованию визуальный актив с ощущением объёма.
**Идеи для экспериментов:**
1. Попробуйте анимировать параметры (например, плавно меняйте noiseCells или noiseNormalScale) в функции update(). Это может создать эффект "плавления" рельефа или медленно движущихся облаков.
2. Создайте несколько слоёв шума с разными параметрами и наложите их друг на друга с помощью blend-режимов (setBlendMode).
3. Используйте сгенерированную текстуру не как самостоятельный объект, а как маску или карту нормалей для другого графического объекта, например, спрайта планеты.
