Заморозка материалов: freeze и unfreeze

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

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

Основные методы:

  • material.freeze()
  • material.unfreeze()

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


Причины появления механизма freeze

Во время рендеринга Babylon.js выполняет ряд операций для каждого материала:

  1. Проверка изменения параметров.
  2. Подготовка define-переменных шейдера.
  3. Проверка состояния текстур.
  4. Перекомпиляция эффекта при изменении конфигурации.
  5. Обновление uniform-переменных.

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

Заморозка отключает:

  • проверку define-флагов
  • перерасчёт эффектов
  • обновление ряда внутренних состояний

В результате уменьшается нагрузка на CPU и повышается стабильность кадровой частоты.


Метод freeze()

Метод freeze() переводит материал в состояние неизменяемости.

Пример:

const material = new BABYLON.StandardMaterial("mat", scene);
material.diffuseColor = new BABYLON.Color3(1, 0, 0);

material.freeze();

После вызова:

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

Внутри движка устанавливается флаг:

material.isFrozen === true

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


Внутренний механизм работы

При стандартном рендеринге Babylon.js выполняет процедуру подготовки материала через метод:

isReadyForSubMesh

В этом методе:

  1. проверяются defines;
  2. формируется конфигурация шейдера;
  3. подготавливается эффект (Effect);
  4. обновляются uniform-параметры.

Если материал заморожен, значительная часть этой логики пропускается.

Упрощённая схема:

Обычный материал

каждый кадр
    проверка параметров
    обновление defines
    проверка текстур
    обновление шейдера

Замороженный материал

каждый кадр
    использование уже подготовленного эффекта

Это особенно эффективно в сценах со статическими объектами.


Метод unfreeze()

Метод unfreeze() снимает ограничение и возвращает материал в обычный режим работы.

material.unfreeze();

После вызова:

  • движок снова отслеживает изменения;
  • разрешается изменение параметров;
  • при необходимости пересобираются шейдеры.

Флаг состояния:

material.isFrozen === false

Практический пример

Создание статической сцены с большим количеством объектов.

const material = new BABYLON.StandardMaterial("groundMat", scene);
material.diffuseTexture = new BABYLON.Texture("ground.jpg", scene);

const ground = BABYLON.MeshBuilder.CreateGround("ground", {
    width: 100,
    height: 100
}, scene);

ground.material = material;

material.freeze();

Если параметры материала не изменяются во время выполнения, заморозка избавляет от повторных вычислений.


Когда применять заморозку

Заморозка эффективна в следующих случаях:

Статические окружения

  • стены
  • здания
  • ландшафт
  • декорации

Большие сцены

  • архитектурная визуализация
  • города
  • интерьерные модели

Материалы без анимации

  • цвет не меняется
  • текстуры не обновляются
  • параметры освещения фиксированы

При наличии сотен мешей, использующих один материал, экономия CPU может быть заметной.


Когда freeze использовать нельзя

Заморозка недопустима, если материал изменяется во время работы сцены.

Типичные ситуации:

Анимация параметров

material.diffuseColor = new BABYLON.Color3(0,1,0);

Изменение текстур

material.diffuseTexture = new BABYLON.Texture("newTexture.jpg", scene);

Переключение прозрачности

material.alpha = 0.5;

Динамическое освещение

Если шейдер зависит от часто изменяемых параметров.

В подобных случаях материал необходимо сначала разморозить:

material.unfreeze();
material.diffuseColor = new BABYLON.Color3(0,1,0);
material.freeze();

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

Для диагностики используется свойство:

material.isFrozen

Пример:

if (!material.isFrozen) {
    material.freeze();
}

Это полезно при управлении состоянием материалов в сложных сценах.


Заморозка и текстуры

Заморозка предполагает, что состояние текстур остаётся неизменным.

Если текстура является динамической:

  • DynamicTexture
  • VideoTexture
  • RenderTargetTexture

использование freeze() может привести к некорректному отображению, поскольку движок не будет обновлять связанные данные.

Пример проблемы:

const videoTexture = new BABYLON.VideoTexture("video", "movie.mp4", scene);
material.diffuseTexture = videoTexture;

material.freeze();

Видео перестанет корректно обновляться.


Влияние на компиляцию шейдеров

Babylon.js использует систему defines, определяющую конфигурацию шейдера:

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

При изменении параметров движок может перекомпилировать шейдер.

Заморозка фиксирует текущий набор defines.

Пример:

#define DIFFUSE
#define NORMAL
#define SPECULAR

После freeze эти флаги остаются неизменными.


Заморозка и SubMesh

Babylon.js рендерит геометрию через структуру SubMesh.

Каждый SubMesh хранит ссылку на:

  • материал
  • эффект
  • буферы вершин

Когда материал заморожен, SubMesh использует уже подготовленный эффект без повторной проверки.

Это уменьшает:

  • вызовы CPU
  • операции подготовки шейдера

Массовая заморозка материалов

В больших сценах иногда замораживают все статические материалы.

Пример:

scene.materials.forEach(mat => {
    mat.freeze();
});

Перед этим необходимо убедиться, что:

  • текстуры загружены
  • параметры материалов окончательно настроены

Иначе возможны ошибки отображения.


Заморозка после загрузки моделей

При использовании загрузчиков, например SceneLoader, материалы часто настраиваются после загрузки модели.

Типичный порядок действий:

  1. загрузка модели
  2. настройка материалов
  3. назначение текстур
  4. заморозка

Пример:

BABYLON.SceneLoader.Append("models/", "house.glb", scene, function () {

    scene.materials.forEach(mat => {
        mat.freeze();
    });

});

Такой подход оптимизирует статические ассеты.


Использование с PBRMaterial

Материалы физически корректного рендеринга (PBRMaterial) содержат больше вычислений:

  • BRDF
  • отражения
  • карты шероховатости
  • карты металличности

Заморозка особенно полезна именно для них.

Пример:

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

pbr.freeze();

В сложных сценах это существенно снижает CPU-нагрузку.


Сравнение производительности

Упрощённая оценка:

Сцена Без freeze С freeze
100 материалов регулярные проверки минимальные проверки
500 материалов высокая CPU нагрузка значительная экономия
1000+ материалов возможны просадки FPS стабильная частота кадров

Эффект зависит от:

  • количества материалов
  • сложности шейдеров
  • числа объектов

Связь с другими оптимизациями Babylon.js

Заморозка материалов часто используется совместно с другими механизмами:

freezeActiveMeshes

scene.freezeActiveMeshes();

instancing

Mesh.createInstance()

thin instances

mesh.thinInstanceAdd()

LOD

mesh.addLODLevel()

Комбинация этих методов позволяет значительно оптимизировать сцены с большим количеством объектов.


Типичная стратегия оптимизации

  1. загрузка сцены
  2. настройка материалов
  3. ожидание загрузки текстур
  4. вызов freeze()
  5. рендеринг сцены

Если требуется изменить параметры:

unfreeze → изменить параметры → freeze

Такая схема позволяет сохранить гибкость и производительность.


Ограничения механизма

Заморозка не влияет на:

  • работу GPU
  • рендеринг вершин
  • освещение сцены
  • работу камеры

Она оптимизирует исключительно CPU-часть подготовки материалов.

В некоторых сценах выигрыш может быть небольшим, если количество материалов невелико.


Краткое резюме принципа работы

Состояния материала:

Normal
   ↓ freeze()
Frozen
   ↓ unfreeze()
Normal

Frozen-состояние означает:

  • фиксированную конфигурацию шейдера
  • отсутствие проверок параметров
  • использование подготовленного эффекта

Это делает freeze() одним из простых и эффективных способов оптимизации рендеринга в Babylon.js.