Babylon.js: инструмент для трехмерной веб-графики
Основы работы с Babylon.js
Как создать базовую 3D сцену с вращающимся кубом?
Babylon.js - это JavaScript фреймворк для создания 3D-графики в браузере с использованием WebGL. Ниже приведен минимальный код, который создает сцену с кубом, подсветкой и камерой.
// Подключаем библиотеку (CDN)
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<canvas id="renderCanvas"></canvas>
<script>
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true);
const createScene = function () {
const scene = new BABYLON.Scene(engine);
// Камера
const camera = new BABYLON.ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 4, 5, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
// Свет
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
// Объект
const box = BABYLON.MeshBuilder.CreateBox('box', { size: 1 }, scene);
// Анимация
scene.registerBeforeRender(function () {
box.rotation.y += 0.01;
});
return scene;
};
const scene = createScene();
engine.runRenderLoop(function () {
scene.render();
});
</script>Babylon js (библиотека babylon.js для 3d)
Возможные проблемы:
- Ошибка загрузки библиотеки - проверьте правильность URL или установите через npm (
npm install babylonjs). - Камера не реагирует на мышь - убедитесь, что
attachControlвызван с правильным элементом canvas.
Как добавить анимацию вращения с помощью ключевых кадров?
Вместо ручного изменения углов в registerBeforeRender можно использовать встроенную систему анимаций Babylon.js.
const animation = new BABYLON.Animation('boxRotate', 'rotation.y', 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
const keys = [];
keys.push({ frame: 0, value: 0 });
keys.push({ frame: 100, value: Math.PI * 2 });
animation.setKeys(keys);
box.animations.push(animation);
scene.beginAnimation(box, 0, 100, true);
Ошибка: анимация не запускается - проверьте, что beginAnimation вызывается после того, как объект добавлен в сцену. Также убедитесь, что частота кадров (30) соответствует ожидаемой скорости.
Как загрузить 3D-модель из файла (GLB/GLTF)?
Babylon.js поддерживает импорт моделей через SceneLoader.
BABYLON.SceneLoader.ImportMesh('', './models/', 'model.glb', scene, function (newMeshes) {
const model = newMeshes[0];
model.position.y = 0.5;
});
Типичная ошибка:
Файл не загружается из-за CORS - разместите модель на том же сервере или настройте заголовки. Также убедитесь, что путь к файлу указан верно.
Как добавить физику (падение куба)?
Используйте плагин @babylonjs/inspector с cannon-es или ammo.js. Пример подключения и настройки:
// Подключение (npm install @babylonjs/core @babylonjs/physics-engine-plugin)
import { HavokPlugin } from '@babylonjs/core';
import HavokPhysics from '@babylonjs/havok';
// Асинхронная инициализация
const havokInstance = await HavokPhysics();
const havokPlugin = new HavokPlugin(true, havokInstance);
scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), havokPlugin);
// Создаем физическое тело
box.physicsImpostor = new BABYLON.PhysicsImpostor(box, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 1, restitution: 0.9 }, scene);
Проблема: физика не работает - убедитесь, что плагин импортирован и версия Babylon.js совместима с Havok. Для простых проектов можно использовать cannon-es через npm install @babylonjs/cannon.
Расширенные примеры работы с Babylon.js
Кастомные шейдеры (ShaderMaterial)
Создание материала с собственным вершинным и фрагментным шейдером для эффекта волн на кубе.
// Вершинный шейдер (wave.vertex.fx)
precision highp float;
attribute vec3 position;
uniform mat4 worldViewProjection;
uniform float time;
void main(void) {
vec3 pos = position;
pos.y += sin(pos.x * 2.0 + time) * 0.1;
gl_Position = worldViewProjection * vec4(pos, 1.0);
}
// Фрагментный шейдер (wave.fragment.fx)
precision highp float;
void main(void) {
gl_FragColor = vec4(0.2, 0.6, 1.0, 1.0);
}
// JavaScript
const shaderMaterial = new BABYLON.ShaderMaterial('wave', scene, './shaders/wave', {
attributes: ['position'],
uniforms: ['worldViewProjection', 'time']
});
let time = 0;
scene.registerBeforeRender(() => {
time += 0.01;
shaderMaterial.setFloat('time', time);
});
box.material = shaderMaterial;
Ошибка: шейдер не компилируется - проверьте синтаксис GLSL и правильность путей к файлам .fx. Используйте BABYLON.Effect.Vertex( для загрузки из строки.
Взаимодействие с объектами через клик (PickResult)
Реализация выделения объекта при клике и отображение информации.
canvas.addEventListener('click', function (evt) {
const pickResult = scene.pick(evt.offsetX, evt.offsetY);
if (pickResult.hit && pickResult.pickedMesh === box) {
box.material.emissiveColor = new BABYLON.Color3(1, 0, 0); // Подсветка красным
alert('Куб выбран!');
}
});
При клике на куб его цвет меняется на красный (эмиссионный).
Система частиц (ParticleSystem)
Создание огненного шлейфа за движущимся объектом.
const particleSystem = new BABYLON.ParticleSystem('particles', 2000, scene);
particleSystem.particleTexture = new BABYLON.Texture('fire.png', scene);
particleSystem.emitter = box; // объект испускания
particleSystem.minEmitBox = new BABYLON.Vector3(-0.1, -0.1, -0.1);
particleSystem.maxEmitBox = new BABYLON.Vector3(0.1, 0.1, 0.1);
particleSystem.color1 = new BABYLON.Color4(1, 0.5, 0, 1);
particleSystem.color2 = new BABYLON.Color4(1, 0, 0, 1);
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.3;
particleSystem.minLifeTime = 0.3;
particleSystem.maxLifeTime = 1.5;
particleSystem.emitRate = 100;
particleSystem.start();
Проблема: текстура не отображается - убедитесь, что файл fire.png существует и не содержит прозрачности в неправильном формате. Для частиц рекомендуется использовать PNG с альфа-каналом.
Использование GUI (Babylon.GUI)
Добавление кнопки для переключения вращения куба.
// Создаем GUI сцену
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
// Кнопка
const button = BABYLON.GUI.Button.CreateSimpleButton('rotBtn', 'Вращать/Стоп');
button.width = '150px';
button.height = '40px';
button.color = 'white';
button.background = 'green';
button.top = '20px';
button.left = '20px';
button.onPointerUpObservable.add(() => {
if (box.rotation.y !== undefined) {
// Простая логика: останавливаем регистрированный ранее обработчик
scene.unregisterBeforeRender(animFunction);
}
});
advancedTexture.addControl(button);
// Функция анимации должна быть объявлена отдельно
let isRotating = true;
const animFunction = () => { if (isRotating) box.rotation.y += 0.01; };
scene.registerBeforeRender(animFunction);
В левом верхнем углу появляется зеленая кнопка, по клику вращение останавливается.