О чем этот пример
Шейдеры позволяют выводить графику вашей игры на новый уровень, добавляя динамические эффекты, которые сложно или невозможно реализовать стандартными средствами рендеринга спрайтов. В Phaser 3 работа с шейдерами инкапсулирована в удобный API, который скрывает сложность WebGL, позволяя разработчикам сосредоточиться на творческой части. В этой статье мы разберем, как загрузить и отобразить простой шейдер, связать его с игровым временем и интегрировать с обычными игровыми объектами.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor()
{
super();
}
preload()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.glsl('hsl', 'assets/shaders/hsl.frag');
this.load.image('logo', 'assets/sprites/phaser3-logo-small.png');
}
create()
{
const shader = this.add.shader({
name: 'hsl',
fragmentKey: 'hsl',
setupUniforms: (setUniform, drawingContext) =>
{
setUniform('time', this.game.loop.getDuration());
},
}, 400, 300, this.scale.width, this.scale.width);
this.add.image(400, 300, 'logo');
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка шейдеров и ресурсов
Для работы с шейдерами в Phaser 3 используется загрузчик this.load.glsl(). Этот метод специально предназначен для загрузки текстовых файлов с исходным кодом шейдеров.
preload()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.glsl('hsl', 'assets/shaders/hsl.frag');
this.load.image('logo', 'assets/sprites/phaser3-logo-small.png');
}
Первой строкой в preload мы задаем базовый URL для всех последующих загрузок. Далее загружаем фрагментный шейдер с ключом 'hsl' из файла hsl.frag. Параллельно загружается обычное изображение логотипа. Обратите внимание, что для отрисовки шейдеров требуется контекст WebGL, поэтому в конфигурации игры должен быть указан type: Phaser.WEBGL.
Создание и настройка шейдера
Создание объекта шейдера происходит в методе create с помощью this.add.shader(). Этот метод принимает объект конфигурации и параметры позиционирования.
const shader = this.add.shader({
name: 'hsl',
fragmentKey: 'hsl',
setupUniforms: (setUniform, drawingContext) =>
{
setUniform('time', this.game.loop.getDuration());
},
}, 400, 300, this.scale.width, this.scale.width);
Конфигурационный объект содержит:
* name: произвольное имя шейдера.
* fragmentKey: ключ, под которым был загружен исходный код шейдера (в нашем случае 'hsl').
* setupUniforms: критически важная функция, которая вызывается каждый кадр для обновления uniform-переменных шейдера. Она получает функцию setUniform для установки значений и контекст рендеринга.
Внутри setupUniforms мы передаем шейдеру uniform-переменную time со значением, равным общему времени работы игры в секундах (this.game.loop.getDuration()). Это классический прием для создания анимированных эффектов (например, плавного изменения цвета или волн).
Последние четыре аргумента this.add.shader() задают позицию шейдера по X и Y (400, 300) и его ширину с высотой. Интересно, что в примере и ширина, и высота установлены в this.scale.width, что создаст квадратный шейдер, растянутый на всю ширину окна игры.
Интеграция шейдера с игровым миром
Шейдер в Phaser — это такой же игровой объект (Game Object), как спрайт или текст. Его можно перемещать, масштабировать и накладывать на другие объекты. Порядок добавления объектов определяет их слой (z-index).
this.add.image(400, 300, 'logo');
В примере после создания шейдера на те же координаты (400, 300) добавляется изображение логотипа. Поскольку изображение добавлено позже, оно будет отрисовано поверх шейдера. Это позволяет использовать шейдеры в качестве фона или сложных динамических задних планов под обычными спрайтами. Вы можете экспериментировать с порядком вызовов, чтобы поместить шейдер поверх графики или между слоями.
Конфигурация игры для WebGL
Без правильной конфигурации шейдеры работать не будут. Ключевой параметр — type.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
Установка type: Phaser.WEBGL обязательна для использования шейдеров, так как они требуют контекста WebGL. Если указать Phaser.AUTO, движок попытается использовать WebGL, но откатится к Canvas, если он недоступен, что приведет к ошибке при создании шейдера. Явное указание Phaser.WEBGL гарантирует, что игра запустится только при поддержке необходимого контекста.
Что попробовать дальше
Как видно из примера, базовое использование шейдеров в Phaser 3 сводится к их загрузке, созданию объекта и передаче динамических данных через uniform-переменные. Это открывает огромный простор для экспериментов. Попробуйте изменить размеры шейдера, чтобы он занимал только часть экрана. Передавайте в шейдер не только time, но и позицию мыши (this.input.activePointer.x / y), создавая интерактивные эффекты. Самый интересный шаг — написание или модификация собственных шейдеров на GLSL для создания уникальных визуальных стилей вашей игры.
