Совместимость материалов и шейдеров

Система материалов в Babylon.js построена вокруг абстракции Material, которая определяет способ взаимодействия объекта сцены с источниками света, текстурами и шейдерными программами GPU. Материал связывает геометрию меша с графическим конвейером WebGL, обеспечивая передачу параметров и вычислений, необходимых для визуализации.

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

Совместимость материалов и шейдеров определяется несколькими факторами:

  • типом материала;
  • используемыми текстурами;
  • параметрами освещения сцены;
  • свойствами геометрии;
  • возможностями WebGL.

Внутри движка система компиляции шейдеров динамически создаёт подходящую программу GPU на основе набора defines, определяющих активные функции.


Основные типы материалов

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

StandardMaterial

Классический материал с моделью освещения Blinn–Phong. Поддерживает:

  • диффузные текстуры;
  • зеркальные отражения;
  • карты нормалей;
  • карты эмиссии;
  • прозрачность;
  • освещение от нескольких источников.

Шейдер StandardMaterial формируется динамически в зависимости от включённых текстур и источников света.

const material = new BABYLON.StandardMaterial("mat", scene);
material.diffuseTexture = new BABYLON.Texture("diffuse.jpg", scene);
material.specularColor = new BABYLON.Color3(0.5, 0.5, 0.5);
mesh.material = material;

PBRMaterial

Физически корректный материал, реализующий модель Physically Based Rendering (PBR). Предназначен для реалистичного освещения и взаимодействия с окружающей средой.

Поддерживаемые параметры:

  • albedo (базовый цвет);
  • metalness;
  • roughness;
  • ambient occlusion;
  • отражения окружающей среды;
  • карты нормалей;
  • прозрачность.

Шейдеры PBRMaterial значительно сложнее и используют большое количество условных компиляций.

const pbr = new BABYLON.PBRMaterial("pbr", scene);
pbr.albedoTexture = new BABYLON.Texture("albedo.png", scene);
pbr.metallic = 0.8;
pbr.roughness = 0.2;

mesh.material = pbr;

NodeMaterial

NodeMaterial позволяет конструировать шейдеры через граф узлов. Каждый узел представляет математическую операцию, текстуру или параметр.

Особенности:

  • визуальное проектирование шейдеров;
  • экспорт готового GLSL-кода;
  • модульность;
  • повторное использование узлов.

В результате компиляции NodeMaterial формируется полноценный шейдер WebGL.


Генерация шейдеров

Babylon.js использует систему ShaderMaterial, Effect и MaterialDefines для генерации и компиляции шейдеров.

Процесс включает несколько этапов:

  1. Формирование списка активных параметров.
  2. Создание строковых директив #define.
  3. Подстановка блоков GLSL-кода.
  4. Компиляция программы WebGL.

Пример условной директивы:

#ifdef DIFFUSE
vec4 diffuseColor = texture2D(diffuseSampler, vUV);
#endif

Если текстура диффузии присутствует, движок добавляет define DIFFUSE, активируя соответствующий код.

Это позволяет одной базе шейдера обслуживать множество конфигураций материалов.


ShaderMaterial и пользовательские шейдеры

Для полного контроля над шейдерами используется ShaderMaterial. Этот тип материала напрямую связывает меш с пользовательскими GLSL-программами.

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

Шейдерные файлы:

// vertex shader
attribute vec3 position;
uniform mat4 worldViewProjection;

void main() {
    gl_Position = worldViewProjection * vec4(position, 1.0);
}
// fragment shader
void main() {
    gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}

Совместимость ShaderMaterial определяется:

  • списком атрибутов геометрии;
  • передаваемыми uniform-переменными;
  • поддержкой WebGL.

Совместимость материалов с геометрией

Материал должен соответствовать набору вершинных атрибутов, содержащихся в меше.

Наиболее распространённые атрибуты:

Атрибут Назначение
position координаты вершины
normal нормаль поверхности
uv координаты текстуры
color цвет вершины
tangent касательный вектор для нормалей

Если шейдер ожидает атрибут normal, но геометрия его не содержит, компиляция может завершиться ошибкой или материал будет работать некорректно.

Пример добавления нормалей:

BABYLON.VertexData.ComputeNormals(positions, indices, normals);

Система defines

Defines играют ключевую роль в совместимости материалов и шейдеров.

Они определяют:

  • наличие текстур;
  • использование освещения;
  • наличие теней;
  • поддержку скелетной анимации;
  • прозрачность.

Пример набора defines:

#define DIFFUSE
#define NORMAL
#define SPECULAR
#define LIGHT0
#define SHADOW0

Каждая директива активирует определённый блок GLSL-кода.

Если параметры материала изменяются (например добавляется текстура), Babylon.js пересобирает шейдер.


Оптимизация количества шейдеров

Чрезмерное число вариантов шейдеров приводит к:

  • увеличению времени компиляции;
  • росту потребления памяти;
  • задержкам при рендеринге.

Babylon.js использует кэширование эффектов. Если комбинация defines уже была скомпилирована, движок повторно использует существующий shader program.

Кэш основан на строке defines:

DIFFUSE;NORMAL;LIGHT0

Идентичная строка означает возможность повторного использования программы.


Совместимость с источниками света

Материалы поддерживают различные типы освещения:

  • PointLight
  • DirectionalLight
  • SpotLight
  • HemisphericLight

Каждый источник света добавляет дополнительные вычисления в шейдере.

Пример:

const light = new BABYLON.PointLight(
    "light",
    new BABYLON.Vector3(0, 10, 0),
    scene
);

При добавлении света активируется define:

#define LIGHT0

Если источников света больше, добавляются дополнительные директивы:

LIGHT1
LIGHT2

Некоторые материалы имеют ограничение на количество источников света.


Совместимость с тенями

Тени реализуются через shadow map и требуют дополнительных параметров.

При использовании теней шейдер получает:

  • shadow sampler;
  • матрицу преобразования света;
  • коэффициенты фильтрации.

Активируется define:

#define SHADOWS

Внутри фрагментного шейдера выполняется проверка видимости:

float shadow = computeShadow();
color *= shadow;

Не все материалы поддерживают тени. Например, некоторые упрощённые материалы или пользовательские ShaderMaterial требуют ручной реализации.


Работа с текстурами

Наличие текстур влияет на структуру шейдера.

Основные типы текстур:

Тип Назначение
diffuse базовый цвет
normal рельеф поверхности
specular зеркальный блеск
emissive собственное свечение
opacity прозрачность
reflection отражения

Каждая текстура добавляет соответствующий sampler:

uniform sampler2D diffuseSampler;

Если текстура отсутствует, соответствующий код исключается из шейдера.


Скелетная анимация и совместимость шейдеров

При использовании skinning шейдер должен учитывать трансформации костей.

Включаются дополнительные атрибуты:

  • matricesIndices
  • matricesWeights

И define:

#define NUM_BONE_INFLUENCERS 4

Вершинный шейдер применяет матрицы костей:

vec4 skinnedPosition = boneMatrix * vec4(position, 1.0);

Материал, не поддерживающий skinning, не сможет корректно отображать анимированную модель.


Инстансинг

Babylon.js поддерживает hardware instancing, позволяющий рендерить тысячи объектов одним вызовом.

Для этого шейдер должен поддерживать дополнительные матрицы трансформации.

Активируется define:

#define INSTANCES

Вершинный шейдер использует instanceMatrix:

mat4 finalWorld = world * instanceMatrix;

Материал, не учитывающий этот параметр, не будет совместим с инстансингом.


Ограничения WebGL

Совместимость шейдеров зависит от версии WebGL.

WebGL 1

ограничения:

  • меньшее число uniform-переменных;
  • отсутствие некоторых текстурных форматов;
  • ограничения на циклы.

WebGL 2

добавляет:

  • UBO (Uniform Buffer Objects);
  • multiple render targets;
  • более сложные структуры данных.

Babylon.js автоматически адаптирует шейдеры под доступную версию WebGL.


Проверка совместимости и отладка

При проблемах компиляции шейдера Babylon.js выводит сообщения в консоль браузера.

Типичные причины ошибок:

  • несовпадение атрибутов;
  • отсутствующие uniform-переменные;
  • синтаксические ошибки GLSL;
  • превышение лимитов GPU.

Инструменты диагностики:

  • Inspector Babylon.js
  • WebGL debug extensions
  • просмотр сгенерированного шейдера

Получить код шейдера:

material.getEffect().getVertexShaderSource();
material.getEffect().getFragmentShaderSource();

Это позволяет анализировать окончательный GLSL, сгенерированный движком.


Расширение системы материалов

Babylon.js допускает расширение существующих материалов через MaterialPlugin.

Плагин позволяет:

  • добавлять собственные defines;
  • внедрять код в шейдер;
  • передавать новые параметры.

Пример регистрации:

class CustomPlugin extends BABYLON.MaterialPluginBase {
    constructor(material) {
        super(material, "CustomPlugin", 200);
    }
}

Такой механизм обеспечивает совместимость пользовательских функций с системой стандартных материалов.


Итоговая модель взаимодействия

Совместимость материалов и шейдеров формируется через комбинацию нескольких слоёв:

  1. Материал — задаёт параметры рендеринга.
  2. Defines — описывают активные функции.
  3. Shader generator — формирует GLSL-код.
  4. Effect — компилирует и кэширует программу GPU.
  5. Mesh — предоставляет геометрические атрибуты.

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