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

Однотонные шумы часто выглядят скучно. В этой статье мы разберем, как преобразовать базовый black-and-white cellular noise в красочную и сложную procedural текстуру, используя всего один объект Phaser и встроенный фильтр Gradient Map. Этот подход позволяет создавать уникальные фоны, текстуры материалов или эффекты окружения без использования внешних изображений, что идеально подходит для прототипирования и генерации контента на лету.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    red = new Phaser.Display.Color(255, 0, 0);
    blue = new Phaser.Display.Color(0, 0, 255);
    color1 = new Phaser.Display.Color();
    color2 = new Phaser.Display.Color();
    color3 = new Phaser.Display.Color();
    color4 = new Phaser.Display.Color();
    color5 = new Phaser.Display.Color();

    create()
    {
        const { width, height } = this.scale;

        const noise = this.add.noisecell3d({
            noiseCells: [ 16, 9, 4 ],
            noiseMode: 1 // Render cells as random flat values.
        }, width / 2, height / 2, width, height);

        // Use a black-and-white input to drive a complex color output.
        Phaser.Display.Color.IntegerToColor(0x446688, this.color1);
        Phaser.Display.Color.IntegerToColor(0x000044, this.color2);
        Phaser.Display.Color.IntegerToColor(0x662288, this.color3);
        Phaser.Display.Color.IntegerToColor(0x66aa66, this.color4);
        Phaser.Display.Color.IntegerToColor(0xff8866, this.color5);
        noise.enableFilters().filters.internal.addGradientMap({
            ramp: [
                {
                    colorStart: this.color1,
                    colorEnd: this.color2,
                    colorSpace: 1,
                    size: 0.3
                },
                {
                    colorStart: this.color2,
                    colorEnd: this.color3,
                    colorSpace: 1,
                    size: 0.2,
                },
                {
                    colorStart: this.color3,
                    colorEnd: this.color4,
                    colorSpace: 1,
                    size: 0.2,
                },
                {
                    colorStart: this.color4,
                    colorEnd: this.color5,
                    colorSpace: 1,
                    size: 0.3,
                }
            ]
        });
    }
}

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

const game = new Phaser.Game(config);

Создание базового 3D Cellular Noise

В основе нашей текстуры лежит объект noisecell3d. В отличие от обычного 2D-шума, 3D-вариант добавляет дополнительное измерение, что усложняет итоговый паттерн. Создается он прямо в методе create() сцены.

const noise = this.add.noisecell3d({
    noiseCells: [ 16, 9, 4 ],
    noiseMode: 1 // Render cells as random flat values.
}, width / 2, height / 2, width, height);

Параметр noiseCells — это массив из трех чисел, определяющих количество ячеек шума по осям X, Y и Z соответственно. Чем меньше число, тем крупнее будут ячейки (области одного цвета) в паттерне. noiseMode: 1 указывает, что каждая ячейка должна быть заполнена случайным, но плоским (однородным) значением, а не градиентом. Объект позиционируется по центру экрана и растягивается на всю его ширину и высоту.

Подготовка палитры цветов

По умолчанию шум отрисовывается в градациях серого. Чтобы его раскрасить, нам нужна палитра. В примере используются объекты Phaser.Display.Color, предварительно созданные как поля класса для переиспользования.

Phaser.Display.Color.IntegerToColor(0x446688, this.color1);
Phaser.Display.Color.IntegerToColor(0x000044, this.color2);
// ... и так далее для color3, color4, color5

Статический метод IntegerToColor преобразует шестнадцатеричное представление цвета (например, 0x446688) в объект Phaser.Display.Color и записывает его во второй переданный аргумент. Это эффективный способ перезаписать существующий объект цвета, избегая создания новых экземпляров в основном игровом цикле.

Применение фильтра Gradient Map

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

noise.enableFilters().filters.internal.addGradientMap({
    ramp: [
        {
            colorStart: this.color1,
            colorEnd: this.color2,
            colorSpace: 1,
            size: 0.3
        },
        // ... другие сегменты градиента
    ]
});

Сначала вызываем enableFilters() на объекте шума, чтобы активировать систему фильтров. Затем обращаемся к внутреннему списку фильтров filters.internal и добавляем туда новый фильтр addGradientMap. Ключевой параметр — ramp, массив объектов, описывающих сегменты градиента.

Настройка сегментов градиента

Каждый сегмент в массиве ramp определяет плавный переход от colorStart к colorEnd.

{
    colorStart: this.color1,
    colorEnd: this.color2,
    colorSpace: 1,
    size: 0.3
}

- colorStart и colorEnd: объекты Phaser.Display.Color, задающие начало и конец сегмента. - colorSpace: определяет пространство, в котором вычисляется интерполяция цвета. Значение `1` соответствует HSL-пространству, что дает более яркие и визуально плавные переходы по сравнению с RGB. - size: относительная доля (от 0 до 1), которую этот сегмент занимает во всем градиенте. В примере сумма всех size равна 1.0, что гарантирует полное покрытие диапазона яркости шума.

Как это работает вместе

Фильтр Gradient Map работает по простому принципу: он берет яркость каждого пикселя исходного черно-белого шума (значение от 0 до 1) и сопоставляет его с цветом на построенном градиенте. Темные участки шума (близкие к 0) окрашиваются в цвета начала первого сегмента, светлые (близкие к 1) — в цвета конца последнего сегмента. Параметры noiseCells и noiseMode напрямую влияют на распределение яркостей, а значит, и на итоговое распределение цветов из градиента, создавая иллюзию сложной многослойной текстуры.

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

Комбинация noisecell3d и Gradient Map — мощный инструмент для procedural генерации контента в Phaser. Вы можете экспериментировать: изменяйте значения в noiseCells для получения текстур разного масштаба, пробуйте другие noiseMode, создавайте градиенты с 2, 5 или 10 сегментами для получения абсолютно разных цветовых схем. Такой подход можно использовать для создания динамического неба, текстур земли, воды или магических эффектов прямо во время выполнения игры.