Передача uniform-переменных в шейдер

В PixiJS шейдеры реализуются через объекты PIXI.Shader и PIXI.Program. Uniform-переменные служат для передачи данных с CPU на GPU и обеспечивают динамическую настройку поведения шейдера без его перекомпиляции. Их использование критически важно для создания эффектов, изменяющихся в реальном времени: анимаций, деформаций, цветовых фильтров и текстурных трансформаций.


Определение uniform-переменных

Uniform-переменные объявляются в GLSL коде шейдера в верхней части программы:

// Vertex shader
uniform mat3 u_transform;
uniform float u_time;

// Fragment shader
uniform vec4 u_color;
uniform sampler2D u_texture;
  • float, int — скаляры;
  • vec2, vec3, vec4 — векторы;
  • mat3, mat4 — матрицы;
  • sampler2D — текстура.

Важно, что uniform-переменные сохраняют своё значение между вызовами рендеринга, пока явно не изменены через JavaScript.


Создание шейдера с uniform-переменными

В PixiJS шейдер создаётся через PIXI.Shader с указанием программы и объекта uniform-переменных:

const vertexSrc = `
precision mediump float;
attribute vec2 aVertexPosition;
uniform mat3 u_transform;
void main() {
    vec3 pos = u_transform * vec3(aVertexPosition, 1.0);
    gl_Position = vec4(pos.xy, 0.0, 1.0);
}
`;

const fragmentSrc = `
precision mediump float;
uniform vec4 u_color;
void main() {
    gl_FragColor = u_color;
}
`;

const uniforms = {
    u_color: new Float32Array([1.0, 0.0, 0.0, 1.0]),
    u_transform: new Float32Array([1,0,0,0,1,0,0,0,1])
};

const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, uniforms);

Здесь объект uniforms связывает имена uniform-переменных с их значениями. Использование Float32Array обеспечивает корректную передачу данных на GPU.


Обновление uniform-переменных во время рендеринга

Uniform-переменные можно изменять динамически, что позволяет создавать анимации и интерактивные эффекты:

app.ticker.add((delta) => {
    shader.uniforms.u_time = performance.now() / 1000;
    shader.uniforms.u_color = new Float32Array([
        Math.sin(performance.now() / 500) * 0.5 + 0.5,
        0.0,
        0.0,
        1.0
    ]);
});
  • performance.now() используется для получения текущего времени в миллисекундах.
  • Изменение uniform-переменных на каждый кадр не требует перекомпиляции шейдера.

Особенности передачи текстур

Текстуры в uniform-переменные передаются через PIXI.Texture. В GLSL они объявляются как sampler2D:

const texture = PIXI.Texture.from('image.png');

const uniforms = {
    u_texture: texture
};

const fragmentSrc = `
precision mediump float;
uniform sampler2D u_texture;
varying vec2 vTextureCoord;
void main() {
    gl_FragColor = texture2D(u_texture, vTextureCoord);
}
`;

const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, uniforms);

Внутри фрагментного шейдера texture2D(u_texture, vTextureCoord) возвращает цвет пикселя, соответствующий координате текстуры.


Связывание атрибутов и uniform-переменных

Uniform-переменные работают независимо от атрибутов, которые описывают вершины:

  • Атрибуты (attribute) изменяются для каждой вершины.
  • Uniform — глобальные для всей примитивной фигуры.

Пример комбинирования:

attribute vec2 aVertexPosition;
uniform vec2 u_offset;
void main() {
    gl_Position = vec4(aVertexPosition + u_offset, 0.0, 1.0);
}

В JavaScript можно сдвигать фигуру, изменяя u_offset без модификации вершин:

shader.uniforms.u_offset = new Float32Array([Math.sin(time), 0.0]);

Эффективность использования uniform-переменных

  1. Минимизация пересылки данных: uniform-переменные изменяются только при необходимости.
  2. Повторное использование шейдеров: один шейдер может обслуживать множество объектов с разными uniform-значениями.
  3. Гибкость: поддерживаются сложные эффекты, включая цветовые фильтры, деформации и анимацию текстур.

Советы по типам данных

  • Использовать Float32Array или Int32Array для массивов чисел.
  • Для матриц применять Float32Array размером 9 (mat3) или 16 (mat4).
  • Для цвета в формате RGBA — массив из 4 элементов [r, g, b, a].
  • Для текстур использовать объекты PIXI.Texture напрямую.

Динамическая генерация uniform-переменных

Можно создавать uniform-переменные в цикле для генерации эффектов с множеством источников:

const numLights = 4;
const uniforms = {};
for (let i = 0; i < numLights; i++) {
    uniforms[`u_lightPos${i}`] = new Float32Array([Math.random(), Math.random()]);
    uniforms[`u_lightColor${i}`] = new Float32Array([1.0, 1.0, 1.0]);
}

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


Передача uniform-переменных в PixiJS — основа взаимодействия CPU и GPU, обеспечивающая высокую производительность и гибкость визуальных эффектов. Правильная организация uniform-данных позволяет создавать сложные шейдерные эффекты с минимальными затратами ресурсов и максимальной повторной используемостью шейдеров.