Передача атрибутов, uniform и sampler

Babylon.js предоставляет мощный и гибкий интерфейс для работы с шейдерами, позволяя напрямую управлять атрибутами, uniform-переменными и текстурными сэмплерами. Эти механизмы являются основой для создания настраиваемой визуализации и сложной графики на WebGL.


Атрибуты вершин

Атрибуты (attributes) — это данные, которые передаются в вершинный шейдер для каждой вершины. В Babylon.js стандартные атрибуты включают позиции (position), нормали (normal), координаты текстур (uv) и цвета вершин (color). Они задаются через объект VertexBuffer и привязываются к материалу или кастомному шейдеру.

Пример определения атрибута при создании кастомного шейдера:

const shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
    vertex: "customVertex",
    fragment: "customFragment",
}, {
    attributes: ["position", "normal", "uv"],
    uniforms: ["worldViewProjection"]
});

Ключевой момент: названия атрибутов должны точно совпадать с теми, что используются в GLSL-коде шейдера. Несовпадение приведёт к тому, что данные не будут переданы.


Uniform-переменные

Uniform-переменные — это константы для всех вершин или пикселей в рендере. Они не изменяются для каждой отдельной вершины, но могут изменяться между кадрами. В Babylon.js uniform-переменные задаются через объект ShaderMaterial или через функции setFloat, setVector3, setMatrix и т.д.

Пример передачи uniform-переменной в шейдер:

shaderMaterial.setFloat("time", performance.now() * 0.001);
shaderMaterial.setVector3("lightPosition", new BABYLON.Vector3(10, 20, 30));
shaderMaterial.setMatrix("worldViewProjection", scene.getTransformMatrix());

Uniform-переменные часто используются для передачи:

  • Мировых и проекционных матриц (world, view, projection, worldViewProjection)
  • Параметров освещения (lightPosition, lightColor, lightIntensity)
  • Временных значений для анимации (time, deltaTime)

Важно: uniform-переменные должны быть заранее объявлены в шейдере. Babylon.js автоматически синхронизирует данные при вызове методов set*, но если название не совпадает, данные не будут переданы.


Sampler и текстуры

Sampler-переменные в GLSL позволяют шейдерам получать доступ к текстурным данным. В Babylon.js текстуры передаются через uniform-сэмплеры. Для этого используется класс Texture и метод setTexture объекта ShaderMaterial.

Пример использования текстуры:

const diffuseTexture = new BABYLON.Texture("textures/wood.jpg", scene);
shaderMaterial.setTexture("diffuseSampler", diffuseTexture);

В шейдере GLSL текстура объявляется как uniform sampler2D diffuseSampler; и используется с помощью функции texture2D или texture в новых версиях GLSL:

vec4 color = texture2D(diffuseSampler, uv);

Особенности работы с текстурами в Babylon.js:

  • Можно использовать несколько текстур одновременно, каждая привязывается к отдельному сэмплеру.
  • Текстуры автоматически обрабатываются движком для поддержки mipmaps, фильтрации и сжатия, если они созданы через Babylon.js API.
  • Обновление текстуры в реальном времени происходит через привязку к объекту Texture и его методам (update, readPixels, dispose).

Связь атрибутов, uniform и sampler в одном шейдере

Комплексное использование этих трёх типов данных позволяет создавать гибкие эффекты. Например, можно передать положение вершины через атрибут position, использовать uniform time для анимации смещения и текстуру через sampler для наложения цвета:

attribute vec3 position;
attribute vec2 uv;
uniform mat4 worldViewProjection;
uniform float time;
uniform sampler2D diffuseSampler;

void main() {
    vec3 animatedPos = position + vec3(0.0, sin(time + position.x), 0.0);
    gl_Position = worldViewProjection * vec4(animatedPos, 1.0);
}

Фрагментный шейдер:

varying vec2 vUV;
uniform sampler2D diffuseSampler;

void main() {
    gl_FragColor = texture2D(diffuseSampler, vUV);
}

В Babylon.js для привязки переменных:

shaderMaterial.setFloat("time", performance.now() * 0.001);
shaderMaterial.setTexture("diffuseSampler", diffuseTexture);

Динамическое обновление uniform и атрибутов

Babylon.js позволяет обновлять uniform и атрибуты каждый кадр через функцию рендера:

scene.registerBeforeRender(() => {
    shaderMaterial.setFloat("time", performance.now() * 0.001);
});

Для атрибутов вершин динамическое обновление требует использования VertexBuffer с флагом Dynamic:

const positions = new Float32Array([/* координаты вершин */]);
const vertexBuffer = new BABYLON.VertexBuffer(engine, positions, BABYLON.VertexBuffer.PositionKind, true, false, 3);

Это особенно полезно для анимации мешей, деформации и эффектов частиц.


Рекомендации по организации данных

  1. Атрибуты должны хранить данные, которые изменяются по вершинам.
  2. Uniform-переменные подходят для глобальных параметров сцены и времени.
  3. Sampler используется только для текстур и карт высот.
  4. Использование ShaderMaterial позволяет объединять все три подхода в единый материал.
  5. Для производительности следует минимизировать количество обновляемых uniform-переменных каждый кадр и избегать избыточных текстурных сэмплеров.

Эти механизмы обеспечивают максимальную гибкость при создании пользовательских шейдеров и эффектов в Babylon.js, позволяя управлять каждой вершиной и пикселем с помощью JavaScript.