Freeze World Matrix для статичной геометрии

Каждый объект сцены в Babylon.js обладает собственной системой трансформаций. Положение, вращение и масштаб объединяются в локальную матрицу, после чего вычисляется мировая матрица (World Matrix). Она описывает окончательное положение объекта в мировом пространстве сцены.

Мировая матрица формируется на основе нескольких факторов:

  • позиция (position)
  • вращение (rotation или rotationQuaternion)
  • масштаб (scaling)
  • трансформации родительского объекта
  • дополнительные модификаторы (pivot, billboard, скелетные трансформации)

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

Для статической геометрии подобные вычисления являются избыточными.


Проблема производительности при большом количестве объектов

В типичной сцене может находиться множество объектов, которые никогда не изменяют своё положение:

  • архитектура уровня
  • элементы окружения
  • декорации
  • статические пропсы
  • элементы ландшафта
  • части больших моделей

Если сцена содержит тысячи таких объектов, на каждом кадре происходит:

  1. проверка необходимости пересчёта
  2. возможный пересчёт матрицы
  3. обновление связанных зависимостей

Даже если реального изменения трансформации нет, проверка всё равно выполняется.

Это создаёт:

  • лишние вычисления на CPU
  • дополнительную работу в системе обновления сцены
  • снижение общей производительности при больших сценах

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


Метод freezeWorldMatrix

Babylon.js предоставляет метод:

mesh.freezeWorldMatrix();

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

После заморозки:

  • движок перестаёт проверять изменения трансформаций
  • матрица считается окончательной
  • вычисления пропускаются на каждом кадре

Это существенно снижает нагрузку на CPU в сценах с большим количеством статических объектов.


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

При вызове freezeWorldMatrix() происходит следующий процесс:

  1. текущая мировая матрица вычисляется один раз
  2. результат сохраняется во внутреннем состоянии объекта
  3. флаг _isWorldMatrixFrozen устанавливается в true
  4. дальнейшие попытки обновления игнорируются

Внутренний механизм проверки выглядит концептуально следующим образом:

if (worldMatrixFrozen) {
    return cachedWorldMatrix;
}

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


Пример использования

Статический объект сцены:

const rock = BABYLON.MeshBuilder.CreateBox("rock", { size: 2 }, scene);

rock.position = new BABYLON.Vector3(10, 0, -5);
rock.rotation.y = Math.PI / 4;

rock.freezeWorldMatrix();

После заморозки любые попытки изменить трансформацию не приведут к обновлению положения.


Разморозка мировой матрицы

Если требуется снова изменить трансформацию, используется метод:

mesh.unfreezeWorldMatrix();

После этого объект снова начинает участвовать в системе пересчёта матриц.

Пример:

rock.unfreezeWorldMatrix();

rock.position.x += 5;

rock.freezeWorldMatrix();

Такая схема применяется, когда объект большую часть времени остаётся статичным, но иногда перемещается.


Когда использовать freezeWorldMatrix

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

1. Статическая геометрия уровня

  • здания
  • стены
  • платформы
  • архитектурные элементы

2. Декорации

  • камни
  • деревья
  • обломки
  • статуи

3. Импортированные сцены

Многие модели из 3D-редакторов являются полностью статическими. После загрузки их можно заморозить.

4. Инстансы, которые не двигаются

При использовании большого количества инстансов заморозка помогает дополнительно снизить нагрузку.


Массовая оптимизация сцены

Часто применяется автоматическая заморозка всех статических объектов:

scene.meshes.forEach(mesh => {
    if (!mesh.skeleton && !mesh.parent) {
        mesh.freezeWorldMatrix();
    }
});

Подобный подход используется после загрузки уровня.


Ограничения и особенности

Заморозка мировой матрицы накладывает ряд ограничений.

Игнорирование изменений трансформации

После заморозки любые изменения:

mesh.position.x += 1;
mesh.rotation.y += 0.1;

не будут влиять на реальное положение объекта.


Нельзя менять родительские связи

Если объект получает родителя после заморозки, трансформации могут работать некорректно.

Корректная последовательность:

назначить родителя
задать трансформации
заморозить матрицу

Нельзя использовать с анимацией трансформаций

Анимации, изменяющие:

  • position
  • rotation
  • scaling

не будут работать, пока матрица заморожена.


Взаимодействие с системой родительских объектов

В Babylon.js трансформации могут наследоваться от родителя.

Parent World Matrix
        ×
Local Matrix
        =
Child World Matrix

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

Поэтому заморозку рекомендуется применять только к объектам:

  • без родителя
  • или с полностью статичной иерархией

Использование после импорта моделей

После загрузки моделей из форматов:

  • glTF
  • OBJ
  • FBX

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

Пример:

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

    meshes.forEach(mesh => {
        mesh.freezeWorldMatrix();
    });

});

Это уменьшает количество вычислений при рендере сцены.


Комбинация с другими оптимизациями

Заморозка мировой матрицы особенно эффективна в сочетании с другими методами оптимизации.

1. MergeMeshes

Объединение геометрии снижает количество draw call.

2. Hardware Instancing

Использование GPU для повторяющихся объектов.

3. Freeze Materials

Материалы также можно замораживать:

material.freeze();

4. Freeze Active Meshes

Фиксация набора видимых мешей сцены.

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


Внутренние детали реализации

Babylon.js хранит состояние заморозки в объекте TransformNode. Основной флаг:

_isWorldMatrixFrozen

При его активации:

  • отключается пересчёт computeWorldMatrix
  • отключаются проверки изменений трансформации
  • используется сохранённая матрица

Это особенно важно, потому что computeWorldMatrix() вызывается очень часто — иногда тысячи раз за кадр в сложных сценах.


Практический эффект

В сценах с большим количеством статических объектов применение freezeWorldMatrix может дать:

  • снижение нагрузки на CPU
  • уменьшение времени обновления сцены
  • повышение FPS
  • улучшение масштабируемости сцены

Наиболее заметный эффект наблюдается в сценах с:

  • сотнями или тысячами объектов
  • сложной иерархией
  • большим количеством статических элементов

Типичный сценарий оптимизации уровня

Часто используется следующий рабочий процесс:

  1. загрузка сцены
  2. настройка материалов и освещения
  3. объединение геометрии
  4. оптимизация инстансов
  5. заморозка мировой матрицы статических объектов

Пример:

scene.executeWhenReady(() => {

    scene.meshes.forEach(mesh => {
        if (!mesh.skeleton && !mesh.animations.length) {
            mesh.freezeWorldMatrix();
        }
    });

});

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


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

Некоторые типы объектов не подходят для заморозки мировой матрицы.

Динамические объекты

  • персонажи
  • транспорт
  • физические объекты

Анимированные меши

  • объекты с transform-анимациями

Объекты с физикой

Если позиция управляется физическим движком (например, Ammo.js или Cannon.js), заморозка приведёт к некорректному поведению.

UI-элементы в 3D пространстве

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


Итоговая роль freezeWorldMatrix

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