GrabDuck

Что выбрать для 3D сайта – Three.js или Blend4Web?

:

Я знаю Blender много лет, использую Unity для своих игр, но полный новичок в WebGL. Однако, будущее именно за этой технологией, и неплохо было бы разобраться с ее использованием. К сожалению, экспортер Unity для WebGL пока не рабочий и нужен иной инструмент.

Я не люблю изобретать велосипед и предпочитаю готовые решения. Найденный ранее Blend4Web выглядит очень заманчиво (интеграция с Blender, качественный экспортер и т.д.), но ведь существует немало других библиотек и платформ для работы с WebGL.

Извечный вопрос: что выбрать новичку? Я поработал с платформами Blend4Web и Three.js. Что из этого вышло — читайте дальше.
image

Базовые знания:

  • Blend4Web тесно интегрирован с Blender, поэтому требуется установленный и настроенный соответствующим образом пакет Blender.
  • Three.js — программная библиотека. Умеет импортировать сцены и объекты из того же Blender. Для этого также нужно установить специальный плагин в редактор.

Я не буду подробно рассматривать процесс настройки обеих платформ, но коснусь одного важного момента. Так как системы предназначены исключительно для создания приложений WebGL, то для тестирования их необходимо поднимать локальный сервер. В случае с Blend4Web, после развертывания SDK, вы уже получите рабочий сервер по адресу: localhost:6687 (порт можно изменить в настройках). Для Three.js такой сервер придется устанавливать отдельно. Например, использовать WampServer или выбрать подходящий из папки Utils (SDK Three.js).

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

Работа с Three.js


Думаете — привычный “Hello World”? А вот и нет, специализированное “hello”, но только с точки зрения 3D. В первую очередь меня интересует практическое применение и удобство использования. Поэтому я попробовал сделать самое примитивное — стандартную сцену с объектами, камерой, освещением и простой анимацией.

Кроме того, на изучение и работу с каждой платформой предполагалось затратить не более 3 часов (для чистоты эксперимента). Этакий марафон с нуля.

Начальный выбор пал на Three.js. Итак стартую…

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

Сначала рассмотрим заготовку HTML:

<html>
<head>
<title>Test</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script type="text/javascript" src="three.js"></script>
</head>

<body>
    <div>
        <div id="webGL-container" >
    </div>
    <script type="text/javascript" src="main.js"></script>
</body>
</html>

Эта универсальная заготовка и ничего интересного здесь нет. Библиотека Three.js подключается в строке . Имеется более компактный вариант three.min.js. Однако, я решил остановится на полной версии. Исполняемый код вынесен в отдельный файл main.js. Именно в нем я собираюсь хранить код работы с движком.

Создание кода для Three.js начинается с объявления сцены, камеры и рендера:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();

После этого можно приступить к созданию объектов. В моем случае я остановился на обычном кубе.
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
var cube = new THREE.Mesh( geometry, material );

Создал геометрию, настроил цвет, смешал и вот он Куб. Ах, да, еще нужно добавить его в сцену командой scene.add( cube );

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

cube.rotation.z += 0.1;

Точно также необходимо сместить камеру, ведь по умолчанию она создается в координатах (0,0,0), как и куб.
camera.position.z = 5;

И последний завершающий штрих — это инициализация и вызов рендера. Целиком базовый код тестовый сцены выглядит следующим образом:
$(function() {
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();

renderer.setSize (window.innerWidth,window.innerHeight);
renderer.setClearColor (0x000000);

var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
var cube = new THREE.Mesh( geometry, material );

scene.add( cube );
camera.position.z = 5;
cube.rotation.z += 0.1;

$("#webGL-container").append (renderer.domElement);
renderer.render(scene, camera);
});

Кое-какие моменты в коде я не объяснил, но важно другое, осознание того, что для объявления примитивного куба в сцене использовалось аж 4 строки. Это навеяло грустные размышления о полноценных моделях с текстурами, анимацией.

После знакомства с документацией примерно стали понятны возможности Three.js. Несколько видов камеры, полноценные типы освещения, поддержка материалов, текстур, анимации, сценические эффекты — вполне внушающий доверия список. В основном все это богатство добавляется в сцену указанным ранее способом. Например, лампу можно создать и разместить следующим образом:

var light = new THREE.PointLight(0xffffff);
light.position.set(-100,200,100);
scene.add(light);

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

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

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

Поэтому на плагин я смотрел с большой надеждой. С его помощью можно экспортировать как отдельно взятый объект, так и всю сцену целиком (имеется ввиду только объекты в сцене). Причем с анимацией, костями, текстурами. Это выглядело очень заманчиво. Однако на практике все оказалось сложнее.

Эпопея началась с попытки загрузки всего одной модели. Нет никаких нареканий к работе экспортера в JSON. Код генерируется интуитивно понятно и сложных настроек там нет. Но внедрить его в движок оказалось непросто.

API Three.js содержит более десятка загрузчиков для различных объектов. Естественно, я выбрал универсальный THREE.JSONLoader. Этому способствовала официальная документация и большое количество уроков, которые я просмотрел. Вот только ни черта это не работало!

//инициализация
var loader = new THREE.JSONLoader(); 
loader.load( "plane.json", createScene1 ); 

//обработка после загрузки объекта
function createScene1( geometry, materials ) {
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( materials ) );
    scene.add( mesh );
}

Я экспериментировал с разными настройками экспортера, пробовал иные конструкции загрузчика, почерпнутые из уроков в сети Интернет, альтернативные функции — результата ноль. Консоль упорно мне твердила: “Некорректный JSON ”, а экран показывал девственно чистый вид.

Как вы думаете, где была загвоздка? Смешно сказать — в отсутствии света. Да, это дурацкая ошибка человека, привыкшего работать в полноценных редакторах. В том же Blender или Unity, удаление источников света позволяло видеть объекты в сцене, тут же чистый программный код. Но вот отсутствие в официальных туториалах такой вроде бы явной особенности, попортило немало моей крови. Кстати, ошибка “Некорректный JSON ” так и не пропала. Ну да ладно, перехожу к загрузке сцены.

Собственно — это самое главное, что меня интересовало. Возможность настроить и расставить объекты в Blender, перегнать их в JSON и управлять ими в коде. Поиск в сети Интернет вывел меня на функцию THREE.SceneLoader. Вроде то, что нужно!

Вот только в официальной справке по API подобной функции не нашлось. Дальнейшие поиски в сети принесли информацию, что данная функция была удалена за ненадобностью. Это был крутой облом…

Вариант с расстановкой объектов в Blender и переноса ручками в код я отмел сразу. Продуктивность такого подхода сомнительна. К тому же, пришлось бы учитывать несоответствие систем координат Blender и Three.js. Так что я решил посмотреть предлагаемый разработчиками Three.js редактор. Он доступен по ссылке на официальном сайте.

Итак, что мы имеем. Запускаемый в браузере трехмерный редактор позволяет импортировать модели, расставлять их и выполнять настройку. Обратно можно экспортировать отдельный объект, сцену в виде JSON и цельного приложения с оберткой в HTML. Изучать редактор я не стал, так как до этого не нашел функции загрузки сцены JSON в свой проект. Кроме того, два часа ночи сигнализировало, что время отведенное на Three.js явно закончилось.

Рабочий код тестового приложения, если кому интересно. Экспериментальные конструкции удалил:

$(function() {
var container,scene,camera,render,cube;

init();
render();

function init() { 
container = document.getElementById( 'webGL-container' );
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 10;

scene = new THREE.Scene();

light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 0, 0, 1 ).normalize();
scene.add( light );

var loader = new THREE.JSONLoader();
loader.load( "cube.json", createScene1 );

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );                
}
    
function createScene1( geometry, materials ) {
cube = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( materials ) );
    scene.add( cube );
}
            
            
function render() { 
    requestAnimationFrame( render );
    cube.rotation.y +=0.01; //вращение куба
    renderer.render( scene, camera );
}
});

Работа с Blend4Web


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

Blend4Web предлагает всю часть визуальной работы выполнять в Blender: настраивать сцену, создавать модели и расставлять их, управлять физикой или частицами. Поэтому такой проблемы, как с Three.js, и в помине нет. Но вот получить сложное приложение только визуальными средствами не удастся.

Задача остается все той же — создание простой сцены с несколькими объектами, камерой, источником света. Плюс несложная анимация одного объекта программным путем. И, разумеется, ограничение времени работы 3 часами. Вот и посмотрим, насколько удобной и полной окажется документация, интуитивна работа с API. Поехали…

На официальном сайте имеются четкие указания о расположении файлов проекта в папках SDK. Так проекты Blender хранятся в папке SDK/Blender/ProjectName, а ресурсы и собственно результат экспорта в SDK/Deploy/Assets/ProjectName. Первоначально это выглядит снобизмом и не понимаешь смысла столь жестких требований, но со временем доходит, зачем это нужно.

После установки SDK, автоматически поднимается сервер и становится доступным по адресу localhost:6687. На стартовой странице предлагается документация и примеры, но главное — сервер можно использовать для тестирования своих приложений.

Я создал несложную сцену в Blender с указанными ранее объектами и сохранил все данные, как требует справка SDK. Первый сюрприз поджидал при попытке экспорта в JSON. В настройках экспортера есть опция Run in Viewer и если ее включить, то после экспорта результат немедленно откроется в браузере в специальном просмотрщике. В нем окончательно можно проверить свою сцену и даже выполнить настройку всех параметров объектов или окружения.

Итак, результат экспорта — это два файла: json и bin. Последний содержит массивы данных по моделям. Теперь настало время создать запускаемый файл.

Первый вопрос, который меня озадачил — это не как писать, а куда сохранять скрипты и HTML. Так как разработчики Blend4Web призывают использовать определенные папки для проекта, то и логично было бы предположить, что и для программных частей предусмотрено свое место. Подтверждения этому я не нашел и просто создал файлы в папке с Deploy/Assets/ProjectName.

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

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="b4w.full.min.js"></script>
    <script type="text/javascript" src="main.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        #container_id {
            position: absolute;
            width: 100%;
            height: 100%;
        }

    </style>
</head>

<body>
    <div id="container_id"></div>
</body>

</html>

В HTML ничего интересного нет, поэтому перейдем к main.js. Инициализация движка и загрузка сцены осуществляется в несколько этапов. Сначала подключаем необходимые модули:
var app = b4w.require("app");
var data = b4w.require("data");

Затем выполняем инициализацию:
app.init({
canvas_container_id: "container_id",
physics_enabled: false,
autoresize: true,
callback: load_cb
});

По названию аргументов, я думаю, понятно, что делает функция init. После инициализации происходит вызов функции load_cb. В ней можно разместить загрузку JSON.
function load_cb() {
    data.load("test.json", loaded_cb);
}

Последняя функция в тестовом скрипте — это loaded_cb, которая вызывается после загрузки сцены. Здесь создается логика приложения.
function loaded_cb() {
//что-то там
//например можно включить управление камерой в сцене
    app.enable_controls();
    app.enable_camera_controls();
}

Загрузка сцены в Blend4Web оказалась делом очень простым. Все, что я включил в Blender — движок корректно отобразил. Нет проблем с расстановкой объектов, включением теней, настройкой мешей и т.д. Но вот как дело обстоит с доступом к отдельным объектам? Понятное дело, что можно сделать анимации в Blender и запускать их программно. Я уверен в этом, хотя и не искал. Но мне нужно простое вращение вокруг оси, по принципу выполненному для Three.js (cube.rotation.y +=0.01).

Сделать это оказалось сложнее, чем я рассчитывал. API Blend4Web имеет функцию, позволяющую вызывать пользовательские процедуры каждый кадр, и с этой стороны все в порядке. Однако я не нашел возможности непосредственного вращения по осям. Можно использовать только кватернионы. В связи с этим код получился несколько громоздким:

function render () {
var Cube = scene.get_object_by_name("Cube");
var _angle = 0.05;
var _vec4;
var obj_quat = transform.get_rotation(Cube, _vec4);
quat.rotateY(obj_quat, _angle, obj_quat);
transform.set_rotation_v(Cube, obj_quat);
}

Небольшие итоги


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

Three.js — это популярная библиотека, для которой написано множество уроков, в том числе и на Хабре. Однако, базовые функции создавались мною со скрипом. Баснословное время потрачено на простейшую загрузку модели в сцену. Могли бы, наверное, в официальный туториал внести информацию об обязательных источниках света. Ну, хотя бы, для таких “крутых” программеров, как я.

Самое главное — я привык работать с комфортом и видеть то, что делаю. Мне нужен редактор для подготовки сцены и совсем не интересны проблемы, связанные с перегоном данных для движка. Я сел делать игру, а не ковыряться с неработающими базовыми функциями. Время — деньги, а Three.js отнял его предостаточно.

Ситуация с Blend4Web выглядит гораздо лучше, но не все так блестяще.

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

Много времени ушло на первоначальное чтение документации по части работы с SDK. Все же хотелось сделать так, как советуют разработчики. Например, они предлагают использовать специальный скрипт Python для сборки готового приложения. С его помощью можно упаковать данные, собрать в кучу разбросанные файлы и т.д. Выглядело заманчиво, но у меня не пошло. Выскочили ошибки в скрипте Питона, которые я даже не стал рассматривать.

Документация Three.js изобилует множеством маленьких примеров. К сожалению, для Blend4Web их нет. Поэтому приходилось листать уроки и смотреть обширные листинги. Взял немного с одной страницы, еще чуть-чуть с другой. В итоге написал код, который с успехом мог бы быть уже в примерах документации SDK. Да, есть такое приложение в SDK — Code Snippets, демонстрирующее различные возможности платформы с показом исходников. Но хотелось бы именно маленьких и простых примеров. Как двигать объект, как вращать модель, как привязать выполнение функции к обновлению кадра и т.д. Так, для вращения объекта я взял кусок кода из примера игры на официальном сайте. Но может быть есть более оптимальный вариант вращения, нежели через непосредственное изменение кватернионов? В итоге я использовал только то, что нашел.

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

Сцена Three.js
Сцена Blend4Web