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

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

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

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

Для начала необходимо создать базовый шейдер с использованием Babylon.js. В шейдерах обычно два типа переменных: attribute (для данных, которые различаются для каждой вершины) и uniform (для значений, которые одинаковы для всех вершин и фрагментов на одном кадре). Рассмотрим пример, как создать шейдер с использованием uniform-переменных.

const vertexShaderCode = `
    attribute vec3 position;
    uniform mat4 worldViewProjection;
    void main() {
        gl_Position = worldViewProjection * vec4(position, 1.0);
    }
`;

const fragmentShaderCode = `
    precision highp float;
    uniform vec4 color;
    void main() {
        gl_FragColor = color;
    }
`;

const shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
    vertexElement: "vertexShader",
    fragmentElement: "fragmentShader"
}, {
    attributes: ["position"],
    uniforms: ["worldViewProjection", "color"]
});

В данном примере создаются два шейдера — vertex и fragment. В vertexShader используется uniform-переменная worldViewProjection, которая представляет собой матрицу преобразования. В fragmentShader передается uniform-переменная color, которая задает цвет пикселей.

Передача значений uniform-переменных из JavaScript

После того как шейдеры созданы, нужно передать значения для uniform-переменных из JavaScript-кода. Для этого используется метод setFloat, setColor3, setMatrix, в зависимости от типа данных переменной.

// Установка значения для матрицы worldViewProjection
shaderMaterial.setMatrix("worldViewProjection", camera.getViewMatrix());

// Установка значения для uniform-переменной цвета
shaderMaterial.setColor4("color", new BABYLON.Color4(1, 0, 0, 1)); // красный

В этом примере:

  • Для worldViewProjection передается матрица, полученная из камеры.
  • Для color передается значение красного цвета.

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

Разновидности uniform-переменных

Uniform-переменные могут быть различных типов, и для каждого типа Babylon.js предоставляет свои методы для их передачи.

  1. Числовые значения (float, int): Для передачи чисел используйте методы setFloat, setInt.

    shaderMaterial.setFloat("someFloat", 1.23);
    shaderMaterial.setInt("someInt", 5);
  2. Цвета (Color3, Color4): Для передачи цвета используйте методы setColor3 или setColor4. Эти методы могут принимать как обычные массивы, так и объекты типа BABYLON.Color3 или BABYLON.Color4.

    shaderMaterial.setColor3("diffuseColor", new BABYLON.Color3(1, 0, 0)); // красный
    shaderMaterial.setColor4("ambientColor", new BABYLON.Color4(1, 1, 1, 0.5)); // полупрозрачный белый
  3. Векторы и матрицы (Vector2, Vector3, Matrix): Для передачи векторов и матриц используются методы setVector2, setVector3, setMatrix.

    shaderMaterial.setVector3("lightPosition", new BABYLON.Vector3(10, 5, 0));
    shaderMaterial.setMatrix("worldMatrix", mesh.getWorldMatrix());

Передача текстур в шейдер

Текстуры также могут быть переданы в качестве uniform-переменных в шейдер. Для этого используется метод setTexture, который позволяет передать текстуру, а затем использовать её в фрагментном шейдере.

const texture = new BABYLON.Texture("path/to/texture.jpg", scene);
shaderMaterial.setTexture("diffuseTexture", texture);

В шейдере это будет выглядеть так:

uniform sampler2D diffuseTexture;
void main() {
    gl_FragColor = texture2D(diffuseTexture, uv);
}

Текстура будет передана через uniform-переменную diffuseTexture, и в шейдере она будет использоваться для получения цвета в соответствующем фрагменте.

Применение uniform-переменных для динамических эффектов

Uniform-переменные широко используются для создания различных визуальных эффектов. Например, для динамических изменений цвета объектов в сцене можно передавать значение времени в качестве uniform-переменной.

shaderMaterial.setFloat("time", performance.now() / 1000);

В шейдере это можно использовать для создания эффекта анимации:

uniform float time;
void main() {
    float r = 0.5 + 0.5 * sin(time);
    gl_FragColor = vec4(r, 0.0, 0.0, 1.0);
}

Здесь значение time изменяется со временем, создавая анимацию, в данном случае — плавное изменение красного цвета.

Работа с кастомными шейдерами

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

const customShaderMaterial = new BABYLON.ShaderMaterial("customShader", scene, "path/to/shader", {
    attributes: ["position"],
    uniforms: ["worldViewProjection", "color", "time"]
});

После этого можно задавать значения для этих переменных, как и для стандартных uniform-переменных, используя соответствующие методы для типов данных.

Особенности использования uniform-переменных

  • Производительность: Передача больших данных через uniform-переменные может повлиять на производительность. Важно следить за количеством передаваемых значений, чтобы не перегружать шейдеры.

  • Ограничения по количеству: В зависимости от графической карты и её возможностей существует ограничение на количество uniform-переменных, которые можно использовать в одном шейдере. Обычно это значение составляет несколько сотен, но всегда стоит проверять технические ограничения.

  • Динамические обновления: Uniform-переменные можно обновлять на каждом кадре, что позволяет создавать динамические эффекты. Однако нужно учитывать, что частые обновления могут повлиять на производительность, если их слишком много.

Заключение

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