Storybook-подход к разработке компонентов сцены

Babylon.js предоставляет мощный движок для работы с 3D-графикой в браузере, но при разработке сложных сцен возникает необходимость структурировать код, делать его модульным и легко тестируемым. Storybook-подход позволяет применять принципы компонентного дизайна из фронтенд-разработки к 3D-сценам, превращая отдельные объекты, материалы и эффекты в самостоятельные, повторно используемые компоненты.


Модульная структура сцены

Основной принцип — разделение сцены на компоненты:

  • Геометрия и меши — каждый меш оформляется как отдельный модуль с параметрами позиционирования, масштабирования и материалов.
  • Материалы и шейдеры — материалы оформляются в виде функций-конструкторов, которые возвращают готовый материал с настраиваемыми свойствами.
  • Камеры и свет — настройка камеры и источников света выносится в отдельные компоненты, что позволяет изменять освещение или перспективу без вмешательства в остальные элементы сцены.

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

/scene
 ├─ /meshes
 │   ├─ cube.js
 │   └─ sphere.js
 ├─ /materials
 │   ├─ standardMaterial.js
 │   └─ pbrMaterial.js
 ├─ /lights
 │   └─ directionalLight.js
 ├─ /camera
 │   └─ arcRotateCamera.js
 └─ scene.js

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


Создание компонентного меша

Меши оформляются как функции с возможностью переопределения параметров:

import { MeshBuilder, Vector3, StandardMaterial, Color3 } from "@babylonjs/core";

export function createCube(scene, { size = 1, position = new Vector3(0,0,0), color = Color3.Red() } = {}) {
    const cube = MeshBuilder.CreateBox("cube", { size }, scene);
    cube.position = position;

    const material = new StandardMaterial("cubeMaterial", scene);
    material.diffuseColor = color;
    cube.material = material;

    return cube;
}

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


Инкапсуляция материалов и эффектов

Материалы и эффекты можно оформлять как отдельные компоненты, позволяющие быстро менять стиль объекта:

export function createGlassMaterial(scene, { alpha = 0.5, reflectionTexture = null } = {}) {
    const material = new StandardMaterial("glassMaterial", scene);
    material.alpha = alpha;
    if (reflectionTexture) {
        material.reflectionTexture = reflectionTexture;
    }
    material.specularPower = 64;
    return material;
}

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


Освещение и камеры как отдельные компоненты

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

import { ArcRotateCamera, Vector3, DirectionalLight } from "@babylonjs/core";

export function setupCamera(scene, { target = Vector3.Zero(), alpha = Math.PI/4, beta = Math.PI/3, radius = 10 } = {}) {
    const camera = new ArcRotateCamera("camera", alpha, beta, radius, target, scene);
    camera.attachControl(scene.getEngine().getRenderingCanvas(), true);
    return camera;
}

export function createSunLight(scene, { direction = new Vector3(-1,-2,-1), intensity = 1 } = {}) {
    const light = new DirectionalLight("sun", direction, scene);
    light.intensity = intensity;
    return light;
}

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


Storybook-интеграция для визуального тестирования

Каждый компонент сцены можно превратить в отдельную историю, аналогично Storybook для UI-компонентов:

  • Меши — отдельные истории с параметрами размера, цвета, текстуры.
  • Материалы — истории для сравнения прозрачности, отражения, PBR-эффектов.
  • Свет и камера — истории для тестирования разных настроек освещения и ракурсов.

Пример организации истории для меша:

import { createCube } from "../meshes/cube";

export default {
    title: "Meshes/Cube",
    argTypes: {
        size: { control: 'number' },
        color: { control: 'color' },
    },
};

export const Default = (args) => {
    const scene = new BABYLON.Scene(engine);
    createCube(scene, args);
    return scene;
};

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


Преимущества Storybook-подхода

  1. Повторное использование компонентов — один и тот же меш или материал можно использовать в разных сценах.
  2. Изоляция визуальных изменений — изменения одного элемента не влияют на остальные компоненты сцены.
  3. Быстрое тестирование эффектов — можно отдельно проверять материалы, свет, камеру без полного рендеринга сложной сцены.
  4. Упрощённая документация — каждая история выступает как живой пример работы компонента, что ускоряет обучение и поддержание проекта.

Подход к композиции сцены

Компоненты объединяются в сцены через центральный модуль:

import { createCube } from "./meshes/cube";
import { setupCamera } from "./camera/arcRotateCamera";
import { createSunLight } from "./lights/directionalLight";

export function createScene(engine) {
    const scene = new BABYLON.Scene(engine);

    setupCamera(scene);
    createSunLight(scene);

    createCube(scene, { position: new BABYLON.Vector3(0, 0.5, 0) });
    createCube(scene, { position: new BABYLON.Vector3(2, 0.5, 0), color: BABYLON.Color3.Blue() });

    return scene;
}

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


Заключение по структуре разработки

Storybook-подход в Babylon.js превращает 3D-сцену в набор чётко определённых, модульных компонентов. Меши, материалы, камеры и источники света становятся переиспользуемыми блоками, а визуальные эксперименты упрощаются благодаря изоляции и параметризации. Такой подход улучшает масштабируемость проектов, ускоряет процесс тестирования и повышает качество кода при работе с 3D-графикой в браузере.