О чем этот пример

Эффект "прогрева" (warp) шума — мощный инструмент для генерации сложных текстур и ландшафтов. Встроенный в Phaser объект `NoiseSimplex3D` позволяет управлять количеством итераций этого эффекта. В статье на примере кода разберем, как параметр `noiseWarpIterations` превращает простой шум в детализированные, органичные паттерны, которые можно использовать для воды, облаков или каменных поверхностей.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    create()
    {
        const { width, height } = this.scale;

        this.add.noisesimplex3d({
            noiseCells: [ 4, 9, 4 ],
            noiseWarpAmount: 0.5
        }, width * 1 / 8, height / 2, width / 4, height);
        this.add.noisesimplex3d({
            noiseCells: [ 4, 9, 4 ],
            noiseWarpAmount: 0.5,
            noiseWarpIterations: 2
        }, width * 3 / 8, height / 2, width / 4, height);
        this.add.noisesimplex3d({
            noiseCells: [ 4, 9, 4 ],
            noiseWarpAmount: 0.5,
            noiseWarpIterations: 3
        }, width * 5 / 8, height / 2, width / 4, height);
        this.add.noisesimplex3d({
            noiseCells: [ 4, 9, 4 ],
            noiseWarpAmount: 0.5,
            noiseWarpIterations: 4
        }, width * 7 / 8, height / 2, width / 4, height);

        this.add.text(10, 10, '1 warp iteration', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
        this.add.text(width / 4 + 10, 10, '2 warp iterations', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
        this.add.text(width / 2 + 10, 10, '3 warp iterations', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
        this.add.text(width * 3 / 4 + 10, 10, '4 warp iterations', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 1280,
    height: 720,
    scene: Example
};

const game = new Phaser.Game(config);

Создание базового объекта 3D-шума

В Phaser 3 существует фабричный метод this.add.noisesimplex3d(), который создает и добавляет на сцену объект 3D-шума Перлина (симплекс-шума). Этот метод принимает конфигурационный объект и параметры позиционирования.

this.add.noisesimplex3d(config, x, y, width, height);

В нашем примере для всех объектов задана одинаковая базовая конфигурация: noiseCells определяет «зернистость» шума по осям (X, Y, Z), а noiseWarpAmount — силу искажения.

{
    noiseCells: [ 4, 9, 4 ],
    noiseWarpAmount: 0.5
}

Что такое warp-итерации?

Параметр noiseWarpIterations управляет тем, сколько раз базовый шум будет использоваться для искажения самого себя. Это процесс, похожий на наложение фильтра. - При значении 1 (или его отсутствии) применяется однократное искажение. - При значении 2 шум искажается, а затем результат снова искажается на основе собственных значений. - С каждой новой итерацией паттерн становится сложнее, теряет регулярность и приобретает более естественный, «природный» вид.

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

Сравнение результатов на практике

В примере создается четыре объекта шума с увеличением noiseWarpIterations от 1 до 4. Они размещаются рядом для наглядного сравнения.

// 1 итерация (значение по умолчанию)
this.add.noisesimplex3d({
    noiseCells: [ 4, 9, 4 ],
    noiseWarpAmount: 0.5
}, width * 1 / 8, height / 2, width / 4, height);

// 4 итерации
this.add.noisesimplex3d({
    noiseCells: [ 4, 9, 4 ],
    noiseWarpAmount: 0.5,
    noiseWarpIterations: 4 // Явное указание параметра
}, width * 7 / 8, height / 2, width / 4, height);

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

Настройка и позиционирование

Помимо конфигурации шума, важно правильно задать позицию и размер. Метод this.add.noisesimplex3d() после config объекта принимает стандартные для игровых объектов параметры.

// x, y — координаты центра объекта.
// width, height — его размеры.
this.add.noisesimplex3d(config, x, y, width, height);

В примере координата X для каждого объекта рассчитывается динамически, чтобы равномерно распределить их по ширине экрана:

width * 1 / 8, // Первый объект
width * 3 / 8, // Второй объект
width * 5 / 8, // Третий объект
width * 7 / 8  // Четвертый объект

Все объекты имеют одинаковую высоту (height) и ширину, равную четверти экрана (width / 4).

Что попробовать дальше

Параметр noiseWarpIterations — это ключ к превращению простого шума в сложную, готовую к использованию текстуру. Больше итераций означает больше деталей и менее предсказуемый результат. **Идеи для экспериментов:** 1. Поиграйте с балансом noiseWarpAmount и noiseWarpIterations. Сильное искажение с малым числом итераций даст резкий эффект, а слабое с большим числом — тонкую структуру. 2. Используйте такие текстуры в качестве карт высот для процедурного ландшафта (noiseWarpIterations: 3-4 отлично подойдет для гор). 3. Анимируйте параметр noiseOffset.z во времени, чтобы создать эффект движущихся облаков или волн, и посмотрите, как итерации warp влияют на анимацию.