Графический движок PixiJS построен поверх WebGL, а значит, рендеринг объектов в конечном итоге сводится к выполнению программ на GPU. Эти программы называются шейдерами и пишутся на языке GLSL (OpenGL Shading Language).
Класс Shader в PixiJS представляет собой оболочку над
парой GLSL-программ — вершинным (vertex shader) и
фрагментным (fragment shader) шейдерами — и
обеспечивает их интеграцию в систему рендеринга.
Шейдер управляет:
Любой шейдер в PixiJS состоит из двух частей:
Обрабатывает каждую вершину примитива. Основные задачи:
Пример минимального вершинного шейдера:
precision mediump float;
attribute vec2 aVertexPosition;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
void main() {
vec3 position = projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0);
gl_Position = vec4(position.xy, 0.0, 1.0);
}
Ключевые элементы:
attribute — входные данные от буфера вершин;uniform — глобальные переменные, передаваемые из
JavaScript;gl_Position — обязательная переменная, определяющая
позицию вершины.Обрабатывает каждый пиксель (фрагмент). Определяет цвет итогового изображения.
Минимальный пример:
precision mediump float;
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
gl_FragColor — итоговый цвет фрагмента.
В PixiJS класс PIXI.Shader объединяет:
Базовое создание шейдера:
const vertexSrc = `
precision mediump float;
attribute vec2 aVertexPosition;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
void main() {
vec3 position = projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0);
gl_Position = vec4(position.xy, 0.0, 1.0);
}
`;
const fragmentSrc = `
precision mediump float;
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
`;
const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, {
uColor: [1.0, 0.0, 0.0, 1.0]
});
Метод Shader.from():
Uniform — это параметры, одинаковые для всех вершин или фрагментов во время одного вызова отрисовки.
Поддерживаемые типы:
| GLSL тип | JS формат |
|---|---|
| float | number |
| vec2 | [x, y] |
| vec3 | [x, y, z] |
| vec4 | [x, y, z, w] |
| mat3 | Float32Array(9) |
| sampler2D | Texture |
Пример динамического обновления uniform:
shader.uniforms.uColor = [Math.random(), 0.0, 0.0, 1.0];
PixiJS автоматически синхронизирует значения с GPU перед рендерингом.
Шейдер не используется напрямую для спрайтов. Он применяется через
Mesh:
const geometry = new PIXI.Geometry()
.addAttribute('aVertexPosition', [
-100, -100,
100, -100,
100, 100,
-100, 100
], 2)
.addIndex([0, 1, 2, 0, 2, 3]);
const mesh = new PIXI.Mesh(geometry, shader);
app.stage.addChild(mesh);
Компоненты:
Geometry — описание вершин;Shader — логика обработки;Mesh — связующий объект.Для использования текстуры применяется sampler2D.
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main() {
gl_FragColor = texture2D(uSampler, vTextureCoord);
}
const texture = PIXI.Texture.from('image.png');
const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, {
uSampler: texture
});
PixiJS автоматически связывает текстуру с нужным текстурным юнитом WebGL.
varying используется для передачи данных из vertex
shader во fragment shader.
Пример:
// vertex
varying vec2 vTextureCoord;
attribute vec2 aTextureCoord;
void main() {
vTextureCoord = aTextureCoord;
}
// fragment
varying vec2 vTextureCoord;
GPU автоматически интерполирует значения между вершинами.
PixiJS передаёт в шейдер стандартные uniform-переменные:
translationMatrixprojectionMatrixОни обеспечивают:
Правильный порядок умножения матриц критичен:
projectionMatrix * translationMatrix * position
Shader внутри себя использует объект
Program.
Можно создать программу отдельно:
const program = PIXI.Program.from(vertexSrc, fragmentSrc);
const shader = new PIXI.Shader(program, {
uColor: [0.0, 1.0, 0.0, 1.0]
});
Это полезно при повторном использовании одной GLSL-программы с разными uniform-наборами.
Шейдер может работать с дополнительными атрибутами:
geometry.addAttribute('aCustomValue', [
0.0, 1.0, 0.5, 0.8
], 1);
В vertex shader:
attribute float aCustomValue;
varying float vCustomValue;
void main() {
vCustomValue = aCustomValue;
}
При работе с шейдерами важно учитывать:
if) в fragment shader;mediump, highp).Фрагментный шейдер выполняется для каждого пикселя, поэтому именно он чаще всего становится узким местом.
Класс Filter в PixiJS построен на основе Shader. Фильтр
— это специализированный шейдер, применяемый к уже отрисованной
текстуре.
Отличие:
Shader применяется к геометрии;Filter применяется к рендер-текстуре.Фильтр использует те же GLSL-принципы, но встроен в систему постобработки.
Удаление:
shader.destroy();
Освобождает связанные WebGL-ресурсы.
1. Несоответствие имён атрибутов Имя в
Geometry.addAttribute() должно совпадать с
attribute в GLSL.
2. Несоответствие типов uniform Передача массива вместо числа или наоборот приводит к ошибке компиляции или undefined-поведению.
3. Отсутствие precision в fragment shader WebGL требует явного указания precision.
4. Неверная работа с матрицами Нарушение порядка умножения приводит к искажению координат.
let time = 0;
app.ticker.add(delta => {
time += delta * 0.01;
shader.uniforms.uTime = time;
});
Fragment shader:
uniform float uTime;
void main() {
float brightness = abs(sin(uTime));
gl_FragColor = vec4(brightness, brightness, brightness, 1.0);
}
Шейдер позволяет полностью отказаться от текстур:
vec2 uv = gl_FragCoord.xy / resolution;
float color = sin(uv.x * 10.0);
gl_FragColor = vec4(vec3(color), 1.0);
vec2 distorted = vTextureCoord;
distorted.y += sin(distorted.x * 10.0 + uTime) * 0.02;
gl_FragColor = texture2D(uSampler, distorted);
PixiJS:
Это снижает нагрузку на GPU и CPU при сложных сценах.
Класс Shader в PixiJS предоставляет прямой доступ к
мощности GPU и позволяет реализовывать произвольные графические
алгоритмы: от простого окрашивания до сложных процедурных эффектов,
освещения и динамических искажений. Работа с GLSL требует понимания
графического конвейера, но открывает возможность точного управления
рендерингом на уровне видеокарты.