О чем этот пример
Использование шейдеров в Phaser 3 — это мощный способ добавить уникальные визуальные эффекты, которые невозможно создать стандартными спрайтами и частицами. Этот пример демонстрирует, как создать простой шейдер, который использует текстуру для генерации цвета каждого пикселя, открывая путь к созданию динамических фонов, психоделических переходов или стилизованного освещения. Вы научитесь загружать шейдер в сцену, связывать его с текстурой и понимать основы передачи данных из JavaScript в GLSL-код.
Версия 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('checker', 'assets/pics/checker.png');
}
create ()
{
const frag = `
precision mediump float;
uniform sampler2D iChannel0;
varying vec2 outTexCoord;
void main ()
{
vec4 pixel = texture2D(iChannel0, outTexCoord);
gl_FragColor = vec4(outTexCoord.xyx * pixel.rgb, 1.0);
}
`;
const shader = this.add.shader({
name: 'simpleTexture',
fragmentSource: frag,
initialUniforms: {
iChannel0: 0
}
}, 400, 300, 800, 600, [ 'checker' ]);
// Or, set the texture like this:
// shader.setUniform('iChannel0', 0);
// shader.setTextures([ 'checker' ]);
}
}
const game = new Phaser.Game({
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
});
Подготовка сцены и загрузка ассетов
Как и в любой сцене Phaser, мы начинаем с загрузки необходимых ресурсов. В данном случае нам нужна только одна текстурная карта, которая будет использоваться шейдером в качестве исходных данных для цвета.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('checker', 'assets/pics/checker.png');
}
Метод preload загружает изображение с шахматной текстурой и присваивает ему ключ 'checker'. Эта текстура будет передана в шейдер как uniform-переменная.
Создание шейдера: от кода до объекта
Шейдер создается непосредственно в методе create. Для этого используется метод this.add.shader(). Ключевые компоненты — это GLSL-код фрагментного шейдера и его начальные параметры.
const frag = `
precision mediump float;
uniform sampler2D iChannel0;
varying vec2 outTexCoord;
void main ()
{
vec4 pixel = texture2D(iChannel0, outTexCoord);
gl_FragColor = vec4(outTexCoord.xyx * pixel.rgb, 1.0);
}
`;
Этот код на GLSL определяет, как будет окрашен каждый пиксель. sampler2D iChannel0 — это ссылка на текстуру. outTexCoord — это автоматически переданные координаты текстуры (от 0.0 до 1.0).
1. `vec4 pixel = texture2D(iChannel0, outTexCoord);` — берет цвет пикселя из текстуры по текущим координатам.
2. `gl_FragColor = vec4(outTexCoord.xyx * pixel.rgb, 1.0);` — вычисляет итоговый цвет. Координаты текстуры (красный и зеленый каналы) умножаются на цвет пикселя из текстуры, создавая градиентный эффект, зависящий от положения и исходного изображения.
Инициализация шейдера в Phaser
Созданный код шейдера нужно превратить в игровой объект. Это делает метод this.add.shader().
const shader = this.add.shader({
name: 'simpleTexture',
fragmentSource: frag,
initialUniforms: {
iChannel0: 0
}
}, 400, 300, 800, 600, [ 'checker' ]);
Разберем аргументы:
- **Конфигурационный объект:** name — внутреннее имя, fragmentSource — наш GLSL-код, initialUniforms — задает, что uniform-переменная iChannel0 будет связана с текстурным блоком под индексом 0.
- **Позиция и размер:** 400, 300 — координаты центра шейдера на холсте, 800, 600 — его ширина и высота.
- **Массив текстур:** [ 'checker' ] — ключи текстур, которые будут загружены в шейдер. Индекс в этом массиве (0) соответствует индексу текстурного блока, указанному в initialUniforms.iChannel0.
Альтернативный способ настройки
Phaser предоставляет гибкость. Можно создать шейдер, а уже потом привязать к нему текстуру и униформы.
// Or, set the texture like this:
// shader.setUniform('iChannel0', 0);
// shader.setTextures([ 'checker' ]);
Это полезно, если текстура загружается динамически или нужно менять ее во время выполнения. setUniform привязывает переменную iChannel0 к текстурному блоку 0, а setTextures загружает массив текстур в соответствующие блоки.
Конфигурация игры
Для работы шейдеров критически важно использовать WebGL-рендерер. Это настраивается при создании экземпляра игры.
const game = new Phaser.Game({
type: Phaser.WEBGL, // Обязательно WEBGL, а не AUTO или CANVAS
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
});
Указание type: Phaser.WEBGL гарантирует, что рендерер будет использовать WebGL, что является обязательным условием для выполнения шейдеров на GPU.
Что попробовать дальше
Вы создали свой первый шейдер в Phaser 3, который динамически окрашивает текстуру на основе координат. Это основа для бесчисленных эффектов. Поэкспериментируйте: измените формулу в gl_FragColor (попробуйте sin(outTexCoord.x * 10.0) для полос), передайте в шейдер время через uniform-переменную для анимации или подставьте другую текстуру для создания сложных масок и смешивания.
