О чем этот пример
Создание динамических световых эффектов — ключевой элемент для погружения в игровой мир. В этой статье разберем, как использовать фильтр Normal Tools в Phaser с опцией `outputRatio` для симуляции направленного освещения и бликов на текстурах, используя карты нормалей. Этот подход не требует сложных шейдеров и позволяет гибко управлять светом прямо из кода, оживляя статичные изображения.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
image;
lightNormalTools;
specularNormalTools;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('gold', 'assets/textures/gold.png');
this.load.image('gold-n', 'assets/textures/gold-n.png');
}
create ()
{
// Base image.
this.image = this.add.image(640, 360, 'gold');
Phaser.Actions.FitToRegion(this.image, 1);
const lightImage = this.add.image(640, 360, 'gold-n')
.setBlendMode(Phaser.BlendModes.ADD)
.setScale(this.image.scale)
.enableFilters();
this.lightNormalTools = lightImage.filters.internal.addNormalTools({
outputRatio: true
});
const cm = lightImage.filters.internal.addColorMatrix();
cm.colorMatrix.set([
0.5, 0, 0, 0, 0,
0, 0.4, 0, 0, 0,
0, 0, 0.25, 0, 0,
0, 0, 0, 1, 0,
]);
const specularImage = this.add.image(640, 360, 'gold-n')
.setBlendMode(Phaser.BlendModes.ADD)
.setScale(this.image.scale)
.enableFilters();
this.specularNormalTools = specularImage.filters.internal.addNormalTools({
outputRatio: true,
ratioRadius: 0.1,
});
// Uncomment the following to isolate parts of the image:
// this.image.setVisible(false);
// lightImage.setVisible(false);
// specularImage.setVisible(false);
}
update (time, delta)
{
this.lightNormalTools.ratioVector.set(
Math.cos(time / 1000),
Math.sin(time / 1000),
0.3
);
this.specularNormalTools.ratioVector.set(
Math.cos(time / 1000),
Math.sin(time / 1000),
0.3
);
}
}
const config = {
type: Phaser.WEBGL,
width: 1280,
height: 720,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка текстур
В методе preload загружаются две текстуры: основное цветное изображение (gold) и его карта нормалей (gold-n). Карта нормалей — это специальное изображение, где цвет каждого пикселя кодирует направление нормали (перпендикуляра) к поверхности. Именно эта информация позволяет рассчитать, как свет должен падать на текстуру.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('gold', 'assets/textures/gold.png');
this.load.image('gold-n', 'assets/textures/gold-n.png');
}
Создание базового изображения и слоев для эффектов
В create сначала создается основное изображение gold. Функция Phaser.Actions.FitToRegion масштабирует его под размер региона (в данном случае — 1 пиксель, что, вероятно, означает полное заполнение с сохранением пропорций). Затем создаются два дополнительных изображения, использующих карту нормалей gold-n. Ключевые моменты:
- Оба слоя используют режим наложения Phaser.BlendModes.ADD для сложения цветов с основным изображением.
- Для каждого слоя включаются фильтры методом .enableFilters().
- Фильтр Normal Tools добавляется через filters.internal.addNormalTools.
this.image = this.add.image(640, 360, 'gold');
Phaser.Actions.FitToRegion(this.image, 1);
const lightImage = this.add.image(640, 360, 'gold-n')
.setBlendMode(Phaser.BlendModes.ADD)
.setScale(this.image.scale)
.enableFilters();
Настройка фильтра Normal Tools и Color Matrix
Первому слою (lightImage) добавляется фильтр Normal Tools с параметром outputRatio: true. Это заставляет фильтр выводить не просто обработанную карту нормалей, а результат расчета соотношения (ratio) между направлением света и нормалями текстуры. Это и создает эффект освещения.
К этому же слою добавляется фильтр Color Matrix (addColorMatrix), который затемняет и окрашивает световой эффект, задавая интенсивность по каналам R, G, B. Это позволяет получить цветной свет (в примере — теплый, золотистый).
Второй слой (specularImage) также использует Normal Tools с outputRatio: true, но с дополнительным параметром ratioRadius: 0.1. Этот параметр влияет на «жесткость» или размытость эффекта, что часто используется для имитации бликов (specular highlights) — ярких отражений от глянцевых поверхностей.
this.lightNormalTools = lightImage.filters.internal.addNormalTools({
outputRatio: true
});
const cm = lightImage.filters.internal.addColorMatrix();
cm.colorMatrix.set([
0.5, 0, 0, 0, 0,
0, 0.4, 0, 0, 0,
0, 0, 0.25, 0, 0,
0, 0, 0, 1, 0,
]);
this.specularNormalTools = specularImage.filters.internal.addNormalTools({
outputRatio: true,
ratioRadius: 0.1,
});
Анимация источника света в реальном времени
В методе update динамически изменяется свойство ratioVector у обоих экземпляров Normal Tools. Этот вектор задает направление, откуда падает свет, в 3D-пространстве (x, y, z).
- Компоненты X и Y рассчитываются с помощью Math.cos и Math.sin от времени, что заставляет источник света плавно вращаться по кругу.
- Компонента Z фиксирована на значении 0.3, задавая наклон света.
Изменение этого вектора в реальном времени создает анимацию движущегося источника света и «бегающих» бликов по текстуре.
update (time, delta)
{
this.lightNormalTools.ratioVector.set(
Math.cos(time / 1000),
Math.sin(time / 1000),
0.3
);
this.specularNormalTools.ratioVector.set(
Math.cos(time / 1000),
Math.sin(time / 1000),
0.3
);
}
Отладка и изоляция эффектов
В исходном коде есть закомментированные строки, которые позволяют скрывать разные слои изображения. Это мощный инструмент для отладки и понимания вклада каждого компонента в финальную картинку. Вы можете поочередно скрывать основное изображение, слой освещения или слой бликов, чтобы увидеть, как каждый из них выглядит отдельно.
// Uncomment the following to isolate parts of the image:
// this.image.setVisible(false);
// lightImage.setVisible(false);
// specularImage.setVisible(false);
Что попробовать дальше
Фильтр Normal Tools с опцией outputRatio — это эффективный способ добавить динамическое освещение и блики к 2D-текстурам в Phaser, используя карты нормалей. Для экспериментов попробуйте: изменить значения в Color Matrix для получения света другого цвета; варьировать параметр ratioRadius для создания эффектов от мягкого свечения до резких бликов; анимировать Z-компоненту ratioVector для имитации изменения высоты источника света или использовать несколько источников света с разными векторами.
