Игровой мир WebGL или Three.js vs Babylon.js

  • Tutorial


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

Введение
1. Базовые элементы
2. Группировка
3. Движение
4. Частицы
5. Анимация — 1
6. Анимация — 2
7. Простой ландшафт
8. Статические коллизии
9. Динамические коллизии
10. Импорт моделей
11. Встраивание физических движков
12. Тени, туман
Продолжение — многопользовательский шутер


Введение


Сразу замечу, ничего холиварного, кроме названия, в статье нет. Все задумывалось как просто обзор разных дополнений и библиотек к играм для THREE.JS, а BABYLON.JS описать как еще одну хорошую библиотеку. Но потом, в процессе разработки, стало понятно, что во многом часто происходит дублирование. Например система частиц в three.js выражена неплохим дополнением, а в babylon.js она встроена в саму библиотеку и они немного по разному настраиваются и работают. В итоге получился, скорее, обзор одного и того же в двух разных фреймворках для WebGL.

По понятным причинам сделать детальный разбор всех имеющихся библиотек просто невозможно. Поэтому в введении ограничусь просто небольшим обзором самых распространённых и со свободной лицензией.
  • three.js Первопроходец и самая известная библиотека.
  • babylon.js Достойный конкурент для three.js
  • turbulenz.com Часто упоминается в числе трех самых популярных наряду с babylon и three.js, ну и количество звездочек на github говорит само за себя.
    В основном turbulenz популяризируется как библиотека для создания игрушек, в частности привлек внимание quake

Далее следует ряд менее популярных фреймворков:
  • playcanvas.com Симпатичный фреймворк, симпатичные демки. С Gangnam Style у них неплохая демка получилась.
  • scenejs.org Симпатичная библиотека, наверно создателям часто приходилось делать модели связанные с медициной. Много препарированных примеров. Синтаксис больше похож на инициализацию jquery плагинов.
  • voxel.js
  • www.senchalabs.org/philogl на первый взгляд примеры не произвели ожидаемого впечатления.
  • www.glge.org Еще одна библиотека.
  • www.goocreate.com/blog Онлайн редактор, импортер.
  • www.kickjs.org просто упомяну здесь для статистики.

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

1. Базовые элементы


Сцена


Для начала нам нужно добавить на страницу нашу сцену.
В THREE.JS добавление происходит с добавления в document.bodyrenderer.domElement
var renderer =  new THREE.WebGLRenderer( {antialias:true} );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );


В BABYLON.JS не может быть контейнером для сцены любой div к примеру. Все начинается с создания элемента canvas:
<canvas id="renderCanvas"></canvas>

Как и у three.js также опцией включается antialiasing.
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);

Далее создаем саму сцену:
BABYLON.JS
scene получает параметром engine.
scene = new BABYLON.Scene(engine);

THREE.JS
Сцена создается как бы отдельно. И в неё уже добавляются все элементы сцены.
var scene = new THREE.Scene();
scene.add( sceneMesh );


После этого в THREE.JS renderer вызывается в любой функции с наличием requestAnimationFrame чаще всего называют animate или render, а в BABYLON.JS колбеком в engine.runRenderLoop.
В THREE.JS, чаще всего в animate добавляется все логика движений, например, полет пуль, вращение объектов, беготня ботов и тому подобное.
function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

Как это выглядит в BABYLON.JS, тут как правило иногда добавляют какие то общие конструкции, подсчет частоты кадров, вершин, количества частиц и прочее. Проще говоря, статистику. Для различных анимаций есть красивый хук, подробней об этом в главе про анимацию
 engine.runRenderLoop(function () {
        scene.render();
        stats.innerHTML = "FPS: <b>" +  BABYLON.Tools.GetFps().toFixed() + "</b>
});

Примитивы


После инициализации сцены первое, что можно сделать — это создать примитивы.
Тут все схоже у babylon.js выглядит компактнее добавление на сцену объекта просто опцией, а у three.js простые манипуляции с назначениями материалов выглядят компактнее.
BABYLON.JS
var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
sphere.material = new BABYLON.StandardMaterial("texture1", scene);
sphere.material.diffuseColor = new BABYLON.Color3(1, 0, 0); //красный
sphere.material.alpha = 0.3;

THREE.JS
var cube = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial({ color: 0x00ff00 }) );
scene.add( cube );

Позиция для координат по отдельности указывается одинаково:
 mesh.position.x = 1;
 mesh.position.y = 1;
 mesh.position.z = 1;

А чтоб сразу задать есть отличия, например в THREE.JS можно написать вот так:
mesh.position.set(1, 1, 1);
mesh.rotation.set(1, 1, 1);

А в BABYLON.JS если не подсматривать в отладчик то в основном так:
mesh.position = new BABYLON.Vector3(1, 1, 1);

Камеры


У обеих библиотек самых используемых камер по две, хотя есть дополнительные у babylon.js к примеру есть с разными фильтрами и специально для планшетов и прочих девайсов. Специально для этого обычно еще нужно подключать hand.js
BABYLON.JS
  • FreeCamera — По сути показывает перспективную проекцию, но с возможностью назначать клавиши для управления, что удобно использовать в играх от первого лица, подробнее в главе про Передвижение персонажа
  • ArcRotateCamera — Камера предполагает вращение вокруг заданной оси, с помощью курсора мыши или сенсора, если предварительно подключить hand.js

THREE.JS
  • PerspectiveCamera — камера перспективной проекции, немного упрощенный аналог FreeCamera. Зависит от пропорций и поля зрения и показывает реальный мир.
  • OrthographicCamera — камера ортогональной проекции, показывает все объекты сцены одинаковыми, без пропорций.

Вращать мышкой сцену в three.js помогает плагин OrbitControls.js. В babylon.js похожая возможность есть в ArcRotateCamera.
new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 );

Из дополнительного тут есть еще CombinedCamera — позволяет устанавливать фокусное расстояние объектива и переключаться между перспективной и ортогональной проекциями.
new THREE.CombinedCamera( width, height, fov, near, far, orthoNear, orthoFar )

Освещение


BABYLON.JS
  1. Point Light — Точечный свет, имитирует световое пятно.
  2. Directional Light — Направленный немного рассеянный свет.
  3. Spot Light — Больше похож на имитацию фонарика, например может имитировать движение светила.
  4. HemisphericLight — подходит для имитации реалистичной окружающей среды, равномерно освещает.


//Point Light
new BABYLON.PointLight("Omni0", new BABYLON.Vector3(1, 10, 1), scene);
//Directional Light
new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3(0, -1, 0), scene);
//Spot Light
new BABYLON.SpotLight("Spot0", new BABYLON.Vector3(0, 30, -10), new BABYLON.Vector3(0, -1, 0), 0.8, 2, scene);
//Hemispheric Light
new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 1, 0), scene);

THREE.JS
  1. AmbientLight — представляет общее освещение, применяемое ко всем объектам сцены.
  2. AreaLight представляет пространственный источник света, имеющий размеры — ширину и высоту и ориентированный в пространстве
  3. DirectionalLight — представляет источник прямого (направленного) освещения — поток параллельных лучей в направлении объекта.
  4. HemisphereLight — представляет полусферическое освещение
  5. SpotLight — представляет прожектор.

//ambientLight
var ambientLight = new THREE.AmbientLight( 0x404040 ); 
//AreaLight
areaLight1 = new THREE.AreaLight( 0xffffff, 1 );
areaLight1.position.set( 0.0001, 10.0001, -18.5001 );
areaLight1.width = 10;
//DirectionalLight
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set( 0, 1, 0 );
//PointLight
var pointLight = new THREE.PointLight( 0xff0000, 1, 100 );
pointLight.position.set( 50, 50, 50 );
//PointLight
var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 100, 1000, 100 );

Материалы


Подход к материалам уже довольно сильно разнится, если у three.js есть как бы список возможных материалов, то у babylon.js по сути есть только один материал и к нему применяются разные свойства: прозрачноcть, накладывание текстур с последующим их смещением по осям и тому подобное.
Пара примеров:
BABYLON.JS
// создание материала и назначение текстуры
var materialSphere6 = new BABYLON.StandardMaterial("texture1", scene);
materialSphere6.diffuseTexture = new BABYLON.Texture("./tree.png", scene);

// создание материала и назначение ему цвета и прозрачности
var materialSphere2 = new BABYLON.StandardMaterial("texture2", scene);
materialSphere2.diffuseColor = new BABYLON.Color3(1, 0, 0); //Red
materialSphere2.alpha = 0.3;


THREE.JS
  • MeshBasicMaterial — просто назначает любой цвет примитиву
  • MeshNormalMaterial — материал со свойствами shading, совмещает в себе смешение цветов.
  • MeshDepthMaterial — материал со свойствами wireframe, выглядит черно-белым
  • MeshLambertMaterial — материал для не блестящих поверхностей
  • MeshPhongMaterial — материал для блестящих поверхностей
  • MeshFaceMaterial — может комбинировать другие виды материалов назначать на каждый полигон свой материал.


Для примера базовая сцена для обоих библиотек:
three.js
<html>
    <head>
	<title>My first Three.js app</title>
	<style>
		body { margin: 0; }
		canvas { width: 100%; height: 100% }
	</style>
    </head>
    <body>
	<script src="js/three.min.js"></script>
	<script>
  	     var scene = new THREE.Scene();
             var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
	     var renderer = new THREE.WebGLRenderer();
	     renderer.setSize( window.innerWidth, window.innerHeight );
	     document.body.appendChild( renderer.domElement );
	     var geometry = new THREE.BoxGeometry( 1, 1, 1 );
	     var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
	     var cube = new THREE.Mesh( geometry, material );
	     scene.add( cube );
	     camera.position.z = 5;
	     var render = function () {
		    requestAnimationFrame( render );
		    renderer.render(scene, camera);
	     };
	     render();
	</script>
    </body>
</html>


babylon.js
<!doctype html>
<html>
<head>
   <meta charset="utf-8">
   <title>Babylon - Basic scene</title>
   <style>  #renderCanvas {   width: 100%;  height: 100%;  }   </style>
   <script src="babylon.js"></script>
</head>
<body>
   <canvas id="renderCanvas"></canvas>

   <script type="text/javascript">
      var canvas = document.querySelector("#renderCanvas");
      var engine = new BABYLON.Engine(canvas, true);
      var createScene = function () {
          var scene = new BABYLON.Scene(engine);
          scene.clearColor = new BABYLON.Color3(0, 1, 0);
          var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
          camera.setTarget(BABYLON.Vector3.Zero());
          camera.attachControl(canvas, false);
          var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
          light.intensity = .5;
          var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
          sphere.position.y = 1;
          var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
          return scene;
      };
      var scene = createScene();
      engine.runRenderLoop(function () {  scene.render();   });
   </script>
</body>
</html>


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

2. Группировка


Пожалуй, одна из нужнейших вещей для игры, необходимая для привязки оружия или космолета к камере, оружия к игроку и т.д.
BABYLON.JS
Есть несколько способов группировки. Самый простой и очевидный — это назначение свойства parent. Например, если надо закрепить какой то объект за камерой, то:
var mesh = new BABYLON.Mesh.CreateBox('name', 1.0, scene);
mesh.position = new BABYLON.Vector3( 1, -1, 5);
mesh.parent = camera;

А дальше уже мы управляем камерой от первого лица.
В THREE.JS нам надо создать родительский объект для всех примитивов и в него поместить остальные объекты, а потом уже управлять этим родительским объектом по своему усмотрению:
var parent = new THREE.Object3D();
parent.add( camera );
parent.add( mesh );
parent.position.y = 10;


3. Движение персонажа


Для игр от первого лица нужно чтобы камера, показывающая перспективу, управлялась мышкой и с клавиатуры.
BABYLON.JS
Тут все довольно просто FreeCamera позволяет сразу управлять движением. Достаточно указать camera.detachControl(canvas). Камере можно задать ряд свойств, например, скорость camera.speed = 1 , назначить клавиши «вперед», «назад», «влево», «вправо» и т.д.
camera.keysUp =       [38, 87];
camera.keysDown =  [40, 83];
camera.keysLeft =     [37, 65];
camera.keysRight =   [39, 68];

Следует заметить, что полноценное управление мышью включается только после подключения PointerLock. Камере можно присвоить детей, которые будут вместе с ней ездить. Соответственно, если мы пишем многопользовательскую игру, то лучше передавать координаты камеры camera.position, camera.cameraRotation серверу для управления.
А вот с THREE.JS все гораздо сложнее. Камера сама по себе тут просто камера и чтобы заставить её двигаться нужно прописывать на каждое нажатие клавиши смену позиции. Естественно, о плавности движения также предстоит побеспокоится.
Для управления мышью тоже все непросто — менять, подставлять координаты в mesh.rotation.set(x, y, z) явно недостаточно. Тут нас немного спасает пример из github.io автора three.js. Поэтому остановлюсь лишь на паре деталей. Чтобы вращать мышью камеру с объектом нужно вначале создать один new THREE.Object3D(), внутрь него поместить другой и внутри вращать. Тогда получится видимость поворота вокруг своей оси. Выглядит это все в сокращенном варианте примерно так:
var pitchObject = new THREE.Object3D();
pitchObject.add( camera );
var yawObject = new THREE.Object3D();
yawObject.position.y = 10;
yawObject.add( pitchObject );
var onMouseMove = function ( event ) {
        yawObject.rotation.y -= event.movementX * 0.002;
	pitchObject.rotation.x -= event.movementY * 0.002;
	pitchObject.rotation.x = Math.max( - Math.PI / 2, Math.min( Math.PI / 2, pitchObject.rotation.x ) );
};
document.addEventListener( 'mousemove', onMouseMove, false );

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

4. Частицы



часто необходимы для рисования огня, взрывов, салютов, выстрелов и много еще чего.
В BABYLON.JS система части встроена и изобилует большим количеством настроек. Но для воспроизведения любого эффекта необходимо экспериментировать или бегать по форумам искать понравившийся. Плюс надо привязывать к готовому мешу. Его, конечно, можно сделать невидимым, но можно было бы просто сделать чтобы была возможность просто указать координаты местонахождения.
Пример небольшого костра на babylon.js:
    var particleSystem = new BABYLON.ParticleSystem("particles", 1000, scene);
    particleSystem.particleTexture = new BABYLON.Texture("./img/flare.png", scene);
    particleSystem.emitter = obj; // Начальный объект, от которого начинают появляться частицы
    // Откуда частицы начинают появляться
    particleSystem.minEmitBox = new BABYLON.Vector3(-0.5, 1, -0.5); // Starting all from
    particleSystem.maxEmitBox = new BABYLON.Vector3(0.5, 1, 0.5); // To...
    // Цвет частиц
    particleSystem.color1 = new BABYLON.Color4(1, 0.5, 0, 1.0);
    particleSystem.color2 = new BABYLON.Color4(1, 0.5, 0, 1.0);
    particleSystem.colorDead = new BABYLON.Color4(0, 0, 0, 0.0);
    // Размер от и до каждой частицы
    particleSystem.minSize = 0.3;
    particleSystem.maxSize = 1;
    // Время жизни каждой частицы, берется в рандомном порядке между max и min
    particleSystem.minLifeTime = 0.2;
    particleSystem.maxLifeTime = 0.4;
    // Эмиссия частиц
    particleSystem.emitRate = 600;
    particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
    particleSystem.gravity = new BABYLON.Vector3(0, 0, 0);
    // Направление каждой частицы после появления
    particleSystem.direction1 = new BABYLON.Vector3(0, 4, 0);
    particleSystem.direction2 = new BABYLON.Vector3(0, 4, 0);
    particleSystem.minAngularSpeed = 0;
    particleSystem.maxAngularSpeed = Math.PI;
    // Скорость
    particleSystem.minEmitPower = 1;
    particleSystem.maxEmitPower = 3;
    particleSystem.updateSpeed = 0.007;
    particleSystem.start();


THREE.JS
Подключается с помощью стороннего плагина, но имеет возможность сразу воспроизводить заготовленные эффекты и выставлять время. Место появления частиц можно задать координатами.
Хороший движок частиц для three.js
Наверное, самый хороший движок частиц
Готовые примеры с готовыми настройками

5. Анимация — 1


Как правило, анимация нужна для воспроизведения каких то эффектов, к примеру, движения небесных светил, движения ботов и тому подобного. Есть несколько вариантов как заставить двигаться разные объекты, рассмотрим их по порядку.
BABYLON.JS
Вставить анимацию можно в любое место стандартным для библиотеки способом:
scene.registerBeforeRender(function () {
        mesh.position.x = 100 * Math.cos(alpha);
        donutmesh.position.y = 5;
        mesh.position.z = 100 * Math.sin(alpha);
        alpha += 0.01;
});

Порой бывает удобно для каждого объекта вызывать свою анимацию.
THREE.JS
Для three.js есть возможность заставить что то двигаться только в петле анимации:
var render = function () {
	requestAnimationFrame( render );
	cube.rotation.x += 0.1;
	cube.rotation.y += 0.1;
	renderer.render(scene, camera);
};
render();


6. Анимация — 2


Кроме заранее неопределенной анимации когда не знаешь что куда побежит, есть анимация определенная, когда, к примеру, персонаж при определенных обстоятельствах делает заранее известные два-три шага или сымитировать, к примеру, стрельбу автомата.
BABYLON.JS
Простая анимация с изменением размера бокса:
 // Создаем анимацию изменяющую размер на 30 кадров в минуту
var animationBox = new BABYLON.Animation("tutoAnimation", "scaling.x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT,  BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// настраиваем ключевые кадры анимации
var keys = [];
keys.push({ frame: 0,  value: 1  });
keys.push({ frame: 20,  value: 0.2  });
keys.push({   frame: 100,   value: 1  });
//добавляем ключи в объект анимации
animationBox.setKeys(keys);
box1.animations.push(animationBox);
//запускаем анимацию
scene.beginAnimation(box1, 0, 100, true);

THREE.JS
В основном анимация заключается в манипуляции с geometry.animation.hierarchy и в вызове geometry.animation.hierarchy
Выглядеть может примерно так
var loader = new THREE.JSONLoader();
loader.load( "models/skinned/scout/scout.js", function( geometry ) {
       for ( var i = 0; i < geometry.animation.hierarchy.length; i ++ ) {
		var bone = geometry.animation.hierarchy[ i ];
		var first = bone.keys[ 0 ];
		var last = bone.keys[ bone.keys.length - 1 ];
		last.pos = first.pos;
		last.rot = first.rot;
		last.scl = first.scl;
	}
        geometry.computeBoundingBox();
	THREE.AnimationHandler.add( geometry.animation );
	var mesh = new THREE.SkinnedMesh( geometry, new THREE.MeshFaceMaterial() );
	mesh.position.set( 400, -250 - geometry.boundingBox.min.y * 7, 0 );
	scene.add( mesh );
	animation = new THREE.Animation( mesh, geometry.animation.name );
	animation.play();
});

Небольшой пример: alteredqualia.com/three/examples/webgl_animation_skinning_tf2.html

7. Простой ландшафт


Часто нужно создать какой-то незатейливый окружающий ландшафт, небольшие холмы или горы. И чтобы это было быстро.
BABYLON.JS
Происходит путем совмещения одной оригинальной картинки ландшафта, которая будет фоном, и другой черно-белой — получается как бы выдавливание.
    var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
    groundMaterial.diffuseTexture = new BABYLON.Texture("./img/earth.jpg", scene);
    var ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "./img/heightMap.jpg", 200, 200, 250, 0, 10, scene, false);
    ground.material = groundMaterial;


THREE.JS
Есть дополнения с похожей функциональностью.
Есть простенький вариант, представляющий из себя колечко из горок.
github.com/jeromeetienne/threex.montainsarena
var mesh    = new THREEx.MontainsArena()
scene.add(mesh)


Есть немного посложнее, процедурно сгенерированые поверхности.
var geometry    = THREEx.Terrain.heightMapToPlaneGeometry(heightMap)
THREEx.Terrain.heightMapToVertexColor(heightMap, geometry)
var material    = new THREE.MeshPhongMaterial({ shading :THREE.SmoothShading, vertexColors :THREE.VertexColors});
var mesh    = new THREE.Mesh( geometry, material );
scene.add( mesh );



8. Статические коллизии


Столкновения объектов заранее предусмотренные.
BABYLON.JS
На каждый объект можно выставить:
Но нужно заметить, что, к примеру, выставлять checkCollisions на большое количество объектов или на объемную площадь бесполезно, все будет тормозить.
Лучше написать, что-нибудь вроде:
if ( mesh.position.y < 10 )
       mesh.position.y = 10;

А вокруг каких-то ландшафтных изгибов лучше выстраивать коридоры из невидимых примитивов.
THREE.JS
Придется все-таки вручную проверять. Или с помощью RayCasting

9. Динамические коллизии


Используются когда вы никак точно не можете знать, произойдет ли столкновение какого-то одного объекта с другим. А если произойдет, то нужно как-то на это реагировать.
Все идентично, практически. Примеры попадания шариков имитирующих попадание пули в объекты.
BABYLON.JS
// meshList - массив со списком объектов
scene.registerBeforeRender(function () {
     for (var i=0; i < meshList.length; i++ ){
           if(bullet.intersectsMesh(meshList[i], true))
                console.log('Есть попадание, координаты:' meshList[i].position);
      }
});

THREE.JS
function animate() {
       requestAnimationFrame(animate);
	for(var res = 0; res < meshList.length; res++) {
            var intersections = raycaster.intersectObject(meshList[res]);
            if (intersections.length > 0)
                console.log('Есть попадание, координаты:' ballMeshes[i].position);
    }
	renderer.render(scene, camera);
}


10. Импорт моделей


Тут экспериментировал в основном только в Blender поэтому могу только за него вести речь.
Установка и настройка импорта выглядит одинаково.
  1. Скачиваем експортер для babylon.js по ссылке github.com/BabylonJS/Babylon.js/tree/master/Exporters/Blender
  2. Копируем его в директорию ./Blender/2.XX/scripts/addons
  3. перезапускам blender, и в Файл->Параметры или Ctrl+Alt+U закладка Дополнения настраиваем нужную галочку. После чего в меню появится возможность экспортировать в желаемый формат.


А вот на стадии импорта начинается уже интересное.
BABYLON.JS
Есть пара нюансов. На первый взгляд все просто, при импорте нет никаких дополнительных галочек — нажал кнопку и все. Но потом выясняется, что некоторые модели так и не импортировались, некоторые вроде нормально импортировались, но почему-то не реагируют на команды, смены позиции или маштабирования.
Например, если импортировать в Blender в формате .obj, а потом из Blender экспортировать в .babylon, то высока вероятность, что может нормально не заработать. А если тот же самый процесс повторить с, к примеру, моделью .blend, то вероятность, что она нормально заработает вырастает в разы.
Пример загрузки объекта в babylon c появившемся в последних версиях AssetsManager
//инициализируем  assetsManager
var assetsManager = new BABYLON.AssetsManager(scene);
// добавляем задачу менеджеру
var meshTask = assetsManager.addMeshTask("obj task", "", "./", "obj.babylon");
//любая задача может предоставлять  onSuccess и onError колбеки:
meshTask.onSuccess = function (task) {
    task.loadedMeshes[0].position = new BABYLON.Vector3(0, 0, 0);
}
// assetsManager предоставляет три колбека onFinish, onTaskSuccess, onTaskError
// показываем всю сцену после того как подгрузятся все импортируемые объекты
assetsManager.onFinish = function (tasks) {
    engine.runRenderLoop(function () { scene.render(); });
};

Все текстуры импортируются вместе с объектом. Главное, чтоб лежали рядом.
THREE.JS
У three.js все не так просто выглядит. Есть пара десятков загрузчиков, делающих примерно одно и тоже.
Импорт .die c помощью ColladaLoader
var loader = new THREE.ColladaLoader();
loader.load("obj.dae", function (result) {
    scene.add(result.scene);
});

Импорт .js импортированного с blender
loader = new THREE.JSONLoader();
loader.load( "./model.js", function( geometry ) {
        mesh = new THREE.Mesh( geometry, new THREE.MeshNormalMaterial() );
        mesh.scale.set( 10, 10, 10 );
        mesh.position.y = 150;
        scene.add( mesh );
});

Загрузка .obj
 var loader = new THREE.OBJLoader();
 loader.load( './model.obj', function ( object ){
     scene.add( object );
});

Загрузка сцены
var loader = new THREE.SceneLoader();
  loader.load('jet.json', function(res) {
      scene.add(res.scene);
      renderer.render(res.scene, camera);
  });

Следует заметить что из three.js можно импортировать сцену из .babylon
var loader = new THREE.BabylonLoader( manager );
loader.load( 'models/babylon/skull.babylon', function ( babylonScene ) {
       scene.add( babylonScene );
}, onProgress, onError );

В three.js тоже есть менеджер загрузок:
var manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
        console.log( item, loaded, total );
};
var loader = new THREE.OBJLoader( manager );
loader.load( './model.obj', function (object) {     });


11. Встраивание физических движков.


Как отдельный класс существуют движки, имитирующие столкновения физического мира. Например, машина натыкается на ящик, который отлетает и пару раз перекатывается. Или шарик попадает в ящик, который сдвигается. Естественно, такие возможности гораздо удобнее, чем все это вручную прописывать.

Существует два направление работы с движками. Первое — с помощью разных плагинов, упрощающих интеграцию и, собственно, напрямую.
BABYLON.JS
В основном используется два oimo.js и cannot.js
Использование oimo.js в babylon.js:
//активация
scene = new BABYLON.Scene(engine);
scene.enablePhysics(new BABYLON.Vector3(0,-10,0), new BABYLON.OimoJSPlugin());
//добавляем физические свойства грунту
grount.setPhysicsState({ impostor: BABYLON.PhysicsEngine.BoxImpostor, move:false});
//добавляем физические свойства боксу и сфере
sphere.setPhysicsState({impostor:BABYLON.PhysicsEngine.SphereImpostor, move:true, mass:1, friction:0.5, restitution:0.5});
box.setPhysicsState({impostor:BABYLON.PhysicsEngine.BoxImpostor, move:true, mass:1, friction:0.5, restitution:0.1});

Дополнительно почитать про oimo.js в babylon.js:
blogs.msdn.com/b/davrous/archive/2014/11/18/understanding-collisions-amp-physics-by-building-a-cool-webgl-babylon-js-demo-with-oimo-js.aspx
pixelcodr.com/tutos/oimo/oimo.html
pixelcodr.com/tutos/physics/physics.html
THREE.JS
Для oimo.js есть Расширение

// инициализация мира oimo.js
var onRenderFcts= [];
var world	= new OIMO.World();
//обновление для каждого кадра положения объекта
onRenderFcts.push(function(delta){ world.step() });
// создание IOMO.Body из объекта three.js
var mesh    = new THREE.Mesh( new THREE.CubeGeometry(1,1,1), new THREE.MeshNormalMaterial() )
scene.add(mesh)
var body    = THREEx.Iomo.createBodyFromMesh(world, mesh)
var updater = new THREEx.Iomo.Body2MeshUpdater(body, mesh)
// тогда в каждом кадре будет обновление позиции объекта
updater.update()


12. Тень.


BABYLON.JS
//тень
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.getShadowMap().renderList.push(torus);
ground.receiveShadows = true;
//туман

THREE.JS
//тень
var mesh = new THREE.Mesh( new THREE.BoxGeometry( 1500, 220, 150 ), new THREE.MeshPhongMaterial({color:0xffdd99}));
mesh.position.z = 20;
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add( mesh );
//туман
scene.fog = new THREE.Fog( 0x59472b, 1000, 500 );
//туман - 2
scene.fog = new THREE.FogExp2(0xD6F1FF, 0.0005);



P.S. За кадром осталось много всего, да и этот материал получился довольно растянутым, на мой взгляд. Надеюсь, он поможет тем, кто только начинает изучать возможности WebGL, а также тем, кто выбирает на чем лучше написать игрушку. Спасибо всем кто дочитал до конца.

Используемые материалы.
THREE.JS
API
Схема вызовов при отрисовке сцены в three.js
Русскоязычная справка
Примеры от stemkoski
Сайт с примерами
Больше примеров
Список дополнений



BABYLON.JS
API
Примеры основной функциональности
Форум babylon.js отвечают очень неплохо
Сайт babylon.js с примерами
Документация
Обзорная статья на Хабре
Поделиться публикацией
Комментарии 18
    +2
    Спасибо за обзор. Сам использовал только three.js (в основном мелочь, вроде vimeo.com/83977515), было интересно увидеть babylon.js. Попробую на досуге. В карму плюс ;)
      0
      Есть еще такой редактор clara.io
        +1
        Еще есть такой ресурс www.webgl-game-engines.com
          0
          Спасибо за обзор.

          К минусам обоих библиотек: когда-то сам так писал, но теперь удивляет такой подход — перерисовывать сцену даже если она не изменилась.
            +3
            а как вы будете определять, что стоит перерисовать?! Если камера повернулась или сместилась совсем немного, перерисовывать придётся всё.
              0
              Речь не об отдельных объектах, а о наличии render loop.
                +2
                Например, как в graphics2d: для изменения параметров используются методы (а не свойства) объекта, которые вызывают перерисовку.
                // обычный подход: loop перерисует всё с другим значением параметра
                object.x = 10;
                
                // функция object.x изменяет параметр и вызывает одну перерисовку
                object.x(10);
                


                Вопрос не в том, чтобы перерисовывать часть объектов (это другой вопрос), а чтобы не перерисовывать, когда это не нужно. И вообще исключить любую обработку. Например, если ваша сцена меняется только при движении мыши, очевидно, перерисовка, когда мышь неподвижна, абсолютно бессмысленна. Но здесь она есть.
                +1
                Если я не ошибаюсь, никто не навязывает перерисовку в цикле, вы вольны вызывать ее на свое усмотрение. Но для динамических сцен, где изменения могут происходить самостоятельно (дождь например), такой цикл необходим. Кроме того — это общепринятая практика для игр, и не забывайте что у рекомендуемого THREE.js цикла есть свои достоинства, например: ограничение частоты кадров и отключение когда вы переключаете вкладку в браузере.
                0
                Спасибо за статью. Давно хотел что-нибудь на JS написать в плане игрушек.
                Скажите, а как обстоит дело с обфускацией кода? Это единственный способ защиты?
                  0
                  Ну как вариант разве что можно предложить на игровом движке blender сделать. И дальше просто экспортировать в браузер. Правда не уверен что там гладко все пройдет с многопользовательским вариантом. Но мне кажется что там тоже многое возможно.
                    +1
                    Обфускация, увы, зачастую замедляет код, что в WebGL (по крайней мере, сейчас) вряд ли приемлемо.
                    Если у вас какие-то секретные алгоритмы, не связанные с отрисовкой, проще всего их перенести на сервер.
                    0
                    Чтобы вращать мышью камеру с объектом нужно вначале создать один new THREE.Object3D(), внутрь него поместить другой и внутри вращать. Тогда получится видимость поворота вокруг своей оси. Выглядит это все в сокращенном варианте примерно так:


                    Проблемы с вращением камеры происходят из-за того, что для поворота объекта используются Углы Эйлера
                    Такие повороты некоммутативны и конечное положение системы зависит от порядка, в котором совершаются повороты.
                    wikipedia.org

                    Для решения проблемы можно использовать один объект (или саму камеру) с измененным порядком осей вращения:
                    obj.rotation.order = 'YXZ';
                    
                      0
                      Самое время вспомнить blend4web.
                        0
                        В тексте и в комментариях выше вспоминали blender, blend4web это по сути тот же blender только может с десятком уже готовых сцен в платной версии. Ну и встроенном в него экспортере в веб.
                          0
                          У них есть еще многофункциональный редактор-просмотрщик и сложность проектов не ограничивается отдельными сценами. Вот пример полноценного уровня с физикой, NPC, даже трактором.
                        0
                        Самая первая картинка — это из вашей игрушки или…? :)
                        Давно пользуюсь three.js, на мой взгляд он все таки лидер среди всех остальных библиотек. Если где-то встречается WebGL, то можно почти быть увереным — это three.js.
                        Ее, на мой взгляд, надо рассматривать как графический движок, а не игровой. Тогда все встает на свои места.
                          0
                          Самая первая картинка из стандартного примера ландшафта babylon.js с наложением карты. Мне в написании игрушки показалось самым сложным это все таки не окружающий мир а взаимодействие друг с другом игроков синхронизация и все такое.
                          +1
                          Замечательный обзор. Правда не дает понять о преимуществах и недостатках, не рассказывает в каких проектах лучше использовать babylon.js, а в каких three.js. Формально, одно можно назвать движком, а другое фреймворком.
                          Все написано понятно, я бы с особым вниманием читал это, если бы только начинал в этой сфере.
                          Стоило бы отметить, что babylon.js написан (переписан?) на TypeScript, будет приятно писать игры более-менее внятных объемов. К three.js тоже есть define types, но, если мне не изменяет память, то актуальность версий не сходится. Хотя можно дописывать самостоятельно.
                          Есть еще очень и очень хорошая штука под названием away3d. Это уже не только flash/as3, можно писать для webgl на js/ts. На сайте есть внушающая демка.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое