О чем этот пример
Однотонные шумы часто выглядят скучно. В этой статье мы разберем, как преобразовать базовый 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 сегментами для получения абсолютно разных цветовых схем. Такой подход можно использовать для создания динамического неба, текстур земли, воды или магических эффектов прямо во время выполнения игры.
