Написание кастомного WebGL-пайплайна

Phaser предоставляет мощный механизм для работы с графикой через WebGL, позволяя создавать кастомные пайплайны, которые обрабатывают рендеринг спрайтов и шейдеров на GPU. Кастомные пайплайны позволяют реализовать уникальные визуальные эффекты, которых невозможно достичь стандартными средствами Phaser, включая постобработку сцен, специальные фильтры и сложные анимации.

Создание кастомного пайплайна

В Phaser 3 кастомный пайплайн строится на основе класса Phaser.Renderer.WebGL.Pipelines.SinglePipeline или MultiPipeline, если требуется более сложная обработка. Для начала необходимо создать новый класс, наследующийся от SinglePipeline:

class CustomPipeline extends Phaser.Renderer.WebGL.Pipelines.SinglePipeline {
    constructor(game) {
        super({
            game: game,
            fragShader: `
                precision mediump float;
                uniform sampler2D uMainSampler;
                varying vec2 outTexCoord;

                void main(void) {
                    vec4 color = texture2D(uMainSampler, outTexCoord);
                    color.r = 1.0 - color.r; // Инверсия красного канала
                    gl_FragColor = color;
                }
            `
        });
    }
}

Ключевые моменты:

  • fragShader содержит GLSL-фрагментный шейдер, который будет выполняться на GPU.
  • uMainSampler автоматически подставляется Phaser и представляет текстуру рендеримого объекта.
  • outTexCoord — координаты текущего пикселя.

Регистрация пайплайна

После создания класса пайплайн необходимо зарегистрировать в рендерере Phaser:

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    scene: {
        preload: preload,
        create: create
    },
    pipeline: { CustomPipeline }
};

const game = new Phaser.Game(config);

function create() {
    this.customPipeline = this.renderer.pipelines.add('CustomPipeline', new CustomPipeline(this.game));

    const sprite = this.add.sprite(400, 300, 'myTexture');
    sprite.setPipeline('CustomPipeline');
}

Обратите внимание:

  • Метод pipelines.add позволяет добавить кастомный пайплайн с уникальным именем.
  • Метод setPipeline привязывает пайплайн к конкретному объекту.

Использование uniform-переменных

Для динамического управления эффектами часто применяются uniform-переменные GLSL. Их можно задавать и обновлять из кода Phaser:

this.customPipeline.setFloat1('time', 0);

this.tweens.addCounter({
    from: 0,
    to: 1,
    duration: 2000,
    yoyo: true,
    repeat: -1,
    onUpdate: (tween) => {
        this.customPipeline.setFloat1('time', tween.getValue());
    }
});

Ключевые моменты:

  • setFloat1, setFloat2, setFloat3, setFloat4 позволяют задавать float-переменные для шейдера.
  • Uniform-переменные могут управлять цветом, смещением координат, прозрачностью и другими параметрами.

Кастомизация вершинного шейдера

По умолчанию SinglePipeline использует стандартный вершинный шейдер Phaser. Для полного контроля можно создать собственный вершинный шейдер:

class VertexCustomPipeline extends Phaser.Renderer.WebGL.Pipelines.SinglePipeline {
    constructor(game) {
        super({
            game: game,
            vertShader: `
                attribute vec2 inPosition;
                attribute vec2 inTexCoord;
                uniform mat4 uProjectionMatrix;
                varying vec2 outTexCoord;

                void main(void) {
                    gl_Position = uProjectionMatrix * vec4(inPosition, 0.0, 1.0);
                    outTexCoord = inTexCoord;
                }
            `,
            fragShader: defaultFragShader
        });
    }
}

Важно понимать:

  • Вершинный шейдер управляет позицией и координатами текстуры.
  • uProjectionMatrix обеспечивает корректное отображение объектов на экране.

Комбинирование с пост-эффектами

Кастомный пайплайн удобно использовать для пост-эффектов на всей сцене. Это делается через рендер-таргеты:

const rt = this.add.renderTexture(0, 0, 800, 600);
rt.draw(this.add.sprite(400, 300, 'myTexture'));
rt.setPipeline('CustomPipeline');

Преимущество такого подхода:

  • Все объекты сцены можно рендерить в текстуру и применять один пайплайн.
  • Эффект распространяется на всю картину, а не на отдельные спрайты.

Оптимизация и производительность

Кастомные пайплайны работают на GPU и требуют минимальных операций на CPU. Для оптимизации:

  • Использовать MultiPipeline только при необходимости обработки нескольких текстур.
  • Минимизировать количество uniform-переменных.
  • По возможности избегать ветвлений в GLSL-фрагментных шейдерах.

Примеры сложных эффектов

  1. Сдвиг цветов (Chromatic Shift):

    • Использование нескольких текстурных сэмплов смещённых по координате.
  2. Размытие (Blur):

    • Вычисление среднего соседних пикселей в фрагментном шейдере.
  3. Волновая деформация:

    • Сдвиг координат пикселя по синусоиде с uniform-параметром времени.

Каждый из этих эффектов реализуется через модификацию фрагментного шейдера и передачу параметров через uniform. В сочетании с Phaser это открывает практически неограниченные возможности для кастомной графики.