О чем этот пример
Шейдеры — мощный инструмент для создания уникальных визуальных эффектов в реальном времени, таких как пикселизация, размытие или цветокоррекция. В этой статье мы разберем, как создать и настроить собственный шейдер в Phaser 3, используя встроенный API. Вы научитесь загружать текстуры, определять шейдерные программы и управлять их параметрами (uniforms) прямо из игровой логики.
Версия 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.image('face', 'assets/pics/bw-face.png');
this.load.image('metal', 'assets/textures/alien-metal.jpg');
this.load.image('grass', 'assets/textures/grass.png');
this.load.image('tiles', 'assets/textures/tiles.jpg');
this.load.image('logo', 'assets/sprites/phaser3-logo-small.png');
}
create ()
{
const frag = `
precision mediump float;
uniform vec2 resolution;
uniform float pixelSize;
varying vec2 fragCoord;
void main (void)
{
vec2 uv = fragCoord / resolution.xy;
gl_FragColor = vec4(uv.xy, 1.0);
}
`;
const base = new Phaser.Display.BaseShader(
'pixelate',
frag,
null,
{
pixelSize: { type: '1f', value: 0.2 }
}
);
const shader = this.add.shader(base, 400, 300, 800, 600, [ 'metal' ]);
shader.setUniform('pixelSize.value', 0.2);
console.log(shader);
}
}
const game = new Phaser.Game({
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
});
Загрузка ресурсов и настройка сцены
Как и в любом проекте на Phaser, работа начинается с загрузки ресурсов в методе preload(). В этом примере мы загружаем несколько текстур, которые позже могут быть использованы как входные данные для шейдера.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('face', 'assets/pics/bw-face.png');
this.load.image('metal', 'assets/textures/alien-metal.jpg');
this.load.image('grass', 'assets/textures/grass.png');
this.load.image('tiles', 'assets/textures/tiles.jpg');
this.load.image('logo', 'assets/sprites/phaser3-logo-small.png');
}
Затем, в методе create(), мы создаем конфигурацию игры, указывая использование WebGL-рендерера. Это обязательное условие для работы с шейдерами.
Создание шейдерной программы
Ядро примера — фрагментный шейдер (Fragment Shader), написанный на GLSL. Этот шейдер просто отображает UV-координаты текстуры в виде цвета, создавая градиент.
const frag = `
precision mediump float;
uniform vec2 resolution;
uniform float pixelSize;
varying vec2 fragCoord;
void main (void)
{
vec2 uv = fragCoord / resolution.xy;
gl_FragColor = vec4(uv.xy, 1.0);
}
`;
Шейдер объявляет uniform-переменные resolution (разрешение области отрисовки) и pixelSize (параметр для эффекта, который мы могли бы использовать для пикселизации). Переменная fragCoord автоматически передается шейдеру и содержит координаты текущего пикселя.
Обертка шейдера в Phaser
Чтобы использовать шейдер в Phaser, его нужно обернуть в объект Phaser.Display.BaseShader. Этот объект связывает исходный код шейдера с его параметрами (uniforms).
const base = new Phaser.Display.BaseShader(
'pixelate',
frag,
null,
{
pixelSize: { type: '1f', value: 0.2 }
}
);
Конструктор принимает: имя шейдера, исходный код фрагментного шейдера, исходный код вершинного шейдера (в данном случае null, используется стандартный) и объект с описанием uniform-переменных. Для pixelSize указан тип '1f' (одно вещественное число) и начальное значение 0.2.
Добавление шейдера на сцену и управление параметрами
Созданный базовый шейдер добавляется на сцену как игровой объект с помощью метода this.add.shader().
const shader = this.add.shader(base, 400, 300, 800, 600, [ 'metal' ]);
Параметры метода: объект BaseShader, координаты X и Y центра шейдера, его ширина и высота, а также массив ключей загруженных текстур, которые будут переданы в шейдер. В данном случае передается текстура 'metal'.
После создания объекта шейдера мы можем динамически менять его uniform-переменные с помощью метода setUniform().
shader.setUniform('pixelSize.value', 0.2);
Этот вызов устанавливает значение uniform-переменной pixelSize в 0.2. Имя параметра передается как строка 'pixelSize.value'.
Что попробовать дальше
Вы создали и добавили на сцену кастомный шейдер, управляя его параметрами из кода игры. Для экспериментов попробуйте: изменить исходный код шейдера, чтобы реализовать настоящую пикселизацию; анимировать значение pixelSize с помощью tweens или в update()-цикле; передавать в шейдер другие текстуры из массива загруженных изображений.
