Тема про three.js от mrdoob в свое время проскакивала на хабре, но детально еще не рассматривалась. В этой и(возможно) последующих статьях я постараюсь исправить это упущение. К сожалению, three.js не предоставляет никакой внятной документации, поэтому все знания по нему получены экспериментальным путем и ковырянием спеки WebGL, т.е. любые дополнения автору топика только приветствуются.
В этой статье мы создадим простенькое приложение, которое продемонстрирует базовые возможности этого движка, поработаем с камерой, светом и тенью, а также научим наши объекты перемещаться. В конце статьи ссылка на файлы демки и скриншот. Итак, к делу!
HTML у нас достаточно простенький, не вызывает вопросов и выглядит следующим образом:
В данном примере будем использовать jQuery, однако это абсолютно не критично и вполне можно было бы обойтись и без неё. RequestAnimationFrame.js, — это небольшой хак для обеспечения кроссбраузерной работы(как это ни странно) RequestAnimationFrame (об этом немного ниже).
Теперь перейдем непосредственно к нашему файлу скрипта demoapp.js
Вешаем обработку всего кода внутри файла на событие ready документа
Для поддержания простоты примера оставим стандартные для демок three.js функции, — init() и animate(). В первой у нас инициализация всего, что мы хотели бы увидеть, а вторая будет использовать requestAnimationFrame и вызывать функцию render(), которая уже и будет отрисовывать все изменения. По мере усложнения нашего демо-приложения, его архитектура будет переведена на backbone.js (но это уже тема, которая будет рассмотрена в рамках отдельной статьи).
Основные элементы, которые будут принимать участие в нашей постановке (обратите внимание, основные, но далеко не все, в three.js достаточно богатый выбор действий):
1) Камера ( THREE.Camera)
Это наши “глаза” на импровизированной сцене событий.
Первый параметр показывает удаленность от объекта, второй — отношение сторон ( как правило, используется именно частное от ширины и высоты окна), третий и четвертый, — near/far, параметры, относящиеся к плоскости и используемые при рендеринге.
Либо же можно сразу создать камеру, которая сможет при зажатой левой кнопке мыши крутиться вокруг сцены, при зажатой правой двигаться вверх-вниз, а при зажатой средней, — зумить сцену. Делается это следующим образом (параметры, передаваемые камере достаточно ясно говорят сами за себя по названиям и показаны в коде приложения):
Следует также заметить, что используется Декартова система координат, т.е. ось x направлена от верхнего левого угла экрана в нижний правый, ось z от верхнего правого в нижний левый и ось y от нижней части экрана к верхней соответственно.
2) Сцена (THREE.Scene)
Основной элемент. Именно на сцену мы будем добавлять все созданные нами объекты и плоскости (с помощью .addObject или addChild). Инициализация сцены выглядит следующим образом:
3)Мотор! Мэш (THREE.Mesh)
Именно с помощью мэша составляются почти все объекты в three.js. Для работы с ним нам нужно определить геометрию и материал мэша.
Геометрия, — это объекты, из которых составляется сцена (кубы, сферы, цилиндры, плоскости и прочее. Эта тема является достаточно объемной, поэтому сейчас мы не будем подробно на ней останавливаться). Материал, впрочем, не менее объемный вопрос, поэтому для начала можно лишь уточнить два момента, — для применения материала, указанного в геометрии следует использовать MeshFaceMaterial, для применения в большинстве других случаев вполне может хватить MeshBasicMaterial.
После чего мы можем со спокойной душой добавлять мэш в сцену
Удаляется объект также несложно, с помощью removeObject
4) Рендерер (THREE.WebGLRenderer / CanvasRenderer)
Нам доступно два вида рендереров, — WebGLRenderer и CanvasRenderer. Уже по названиям можно понять, что к чему. Следует также заметить, что, как и ожидалось, WebGLRenderer заметно сильнее роняет fps на слабеньких машинах. Использовать рендереры также предельно просто:
или
Одним из требуемых действий после инициализации рендерера является установка его размера, — здесь мы вольны выбирать какой угодно размер, исходя из наших нужд.
И, наконец, нам нужно добавить элемент рендерера на страницу. Делается это следующим образом и одинаково для всех типов рендереров:
или же для jQuery
5) Свет.
Да, к сцене можно (а иногда и нужно) применять свет, например, для получения теней от объектов (их можно, конечно, сделать и с помощью параллельно движущихся плоскостей, но это скорее в виде исключения). Существует три вида освещения:
5.1) THREE.AmbientLight — это освещение, которое затрагивает всю сцену. Оно не имеет направления и затрагивает каждый объект сцены в равной степени, независимо от расположения объекта. Соответственно, у этого света нет позиции на оси координат
5.2) THREE.PointLight — освещение, исходящее из одной точки во всех направлениях. Думаю, сравнение с обыкновенной лампочкой будет достаточно уместно.
5.3) THREE.DirectionalLight — свет, движущийся в определенном направлении (от заданной точки и к началу координат). Солнечный свет, например, можно отнести именно к этому типу освещения.
В примере мы будем использовать только DirectionalLight для получения тени от размещенного над плоскостью объекта.
Теперь можно перейти непосредственно к коду приложения. В нем мы будем использовать все вышеописанные элементы, а также комментировать производимые действия. Для начала стартуем наше приложение посредством вызова функций инициализации и анимации, а также объявим нужные нам глобальные переменные.
Рассмотрим функцию инициализации.
Выполнение анимации:
Эта функция является стандартной частью большинства приложений на three.js. Рассмотрим её поближе. requestAnimationFrame (animate) представляет собой функцию для обеспечения кроссбраузерной работы анимации (подробнее даже вот тут) и обеспечивает рекурсивный вызов функции render(), которая, собственно, и рендерит всё это наше счастье.
Рендеринг
Именно здесь нужно настраивать перемещения камеры и объектов (разумеется, заранее нужно настроить область видимости переменных таким образом, чтобы они были видны в этой функции)
Следует обратить внимание, что в примере использовалась билд версия three.js, взятая с github (по сравнению с обычной версией изменен вызов геометрии фигур, вместо THREE.xxx THREE.xxxGeometry, например THREE.CubeGeometry на старой версии three.js работать не будет, но будет работать THREE.Cube. Также старая версия не понимает THREE.TrackBallCamera).
Мы создали наше первое простенькое демо, ознакомились с базовыми компонентами, без которых невозможно создать ни одно приложение, основанное на three.js, поставили свет и увидели в действии цикл анимации. В дальнейшем мы разберемся с добавлением собственных текстур и моделей объектов, построением сложных сцен, добавлением действий над объектами сцены и переведем это все счастье на backbone.js. Исходный код приложения доступен по ссылке.

В этой статье мы создадим простенькое приложение, которое продемонстрирует базовые возможности этого движка, поработаем с камерой, светом и тенью, а также научим наши объекты перемещаться. В конце статьи ссылка на файлы демки и скриншот. Итак, к делу!
Первые шаги
HTML у нас достаточно простенький, не вызывает вопросов и выглядит следующим образом:
<!DOCTYPE html>
<html>
<head>
<title>Three.js - dice</title>
<meta charset="utf-8">
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
<!-- libs -->
<script type="text/javascript" src="js/Three.js"></script>
<script type="text/javascript" src="js/RequestAnimationFrame.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
<!-- app -->
<script type="text/javascript" src="js/demoapp.js"></script>
</head>
<body>
</body>
</html>
В данном примере будем использовать jQuery, однако это абсолютно не критично и вполне можно было бы обойтись и без неё. RequestAnimationFrame.js, — это небольшой хак для обеспечения кроссбраузерной работы(как это ни странно) RequestAnimationFrame (об этом немного ниже).
Теперь перейдем непосредственно к нашему файлу скрипта demoapp.js
Вешаем обработку всего кода внутри файла на событие ready документа
$(document).ready(function() { /**код внутри**/});
Для поддержания простоты примера оставим стандартные для демок three.js функции, — init() и animate(). В первой у нас инициализация всего, что мы хотели бы увидеть, а вторая будет использовать requestAnimationFrame и вызывать функцию render(), которая уже и будет отрисовывать все изменения. По мере усложнения нашего демо-приложения, его архитектура будет переведена на backbone.js (но это уже тема, которая будет рассмотрена в рамках отдельной статьи).
Основные элементы
Основные элементы, которые будут принимать участие в нашей постановке (обратите внимание, основные, но далеко не все, в three.js достаточно богатый выбор действий):
1) Камера ( THREE.Camera)
Это наши “глаза” на импровизированной сцене событий.
Первый параметр показывает удаленность от объекта, второй — отношение сторон ( как правило, используется именно частное от ширины и высоты окна), третий и четвертый, — near/far, параметры, относящиеся к плоскости и используемые при рендеринге.
camera = new THREE.Camera(30, window.innerWidth/window.innerHeight,1, 3000);
Либо же можно сразу создать камеру, которая сможет при зажатой левой кнопке мыши крутиться вокруг сцены, при зажатой правой двигаться вверх-вниз, а при зажатой средней, — зумить сцену. Делается это следующим образом (параметры, передаваемые камере достаточно ясно говорят сами за себя по названиям и показаны в коде приложения):
camera = new THREE.TrackballCamera({/**parameters**/});
Следует также заметить, что используется Декартова система координат, т.е. ось x направлена от верхнего левого угла экрана в нижний правый, ось z от верхнего правого в нижний левый и ось y от нижней части экрана к верхней соответственно.
2) Сцена (THREE.Scene)
Основной элемент. Именно на сцену мы будем добавлять все созданные нами объекты и плоскости (с помощью .addObject или addChild). Инициализация сцены выглядит следующим образом:
scene = new THREE.Scene();
3)
Именно с помощью мэша составляются почти все объекты в three.js. Для работы с ним нам нужно определить геометрию и материал мэша.
Геометрия, — это объекты, из которых составляется сцена (кубы, сферы, цилиндры, плоскости и прочее. Эта тема является достаточно объемной, поэтому сейчас мы не будем подробно на ней останавливаться). Материал, впрочем, не менее объемный вопрос, поэтому для начала можно лишь уточнить два момента, — для применения материала, указанного в геометрии следует использовать MeshFaceMaterial, для применения в большинстве других случаев вполне может хватить MeshBasicMaterial.
mesh = new THREE.Mesh( geometry, material );
После чего мы можем со спокойной душой добавлять мэш в сцену
scene.addObject( mesh );//или .addChild
Удаляется объект также несложно, с помощью removeObject
scene.removeObject(mesh);
4) Рендерер (THREE.WebGLRenderer / CanvasRenderer)
Нам доступно два вида рендереров, — WebGLRenderer и CanvasRenderer. Уже по названиям можно понять, что к чему. Следует также заметить, что, как и ожидалось, WebGLRenderer заметно сильнее роняет fps на слабеньких машинах. Использовать рендереры также предельно просто:
renderer = new THREE.WebGLRenderer( );
или
renderer = new THREE.CanvasRenderer();
Одним из требуемых действий после инициализации рендерера является установка его размера, — здесь мы вольны выбирать какой угодно размер, исходя из наших нужд.
renderer.setSize( window.innerWidth, window.innerHeight );
И, наконец, нам нужно добавить элемент рендерера на страницу. Делается это следующим образом и одинаково для всех типов рендереров:
container.appendChild( renderer.domElement );
или же для jQuery
container.append(renderer.domElement);
5) Свет.
Да, к сцене можно (а иногда и нужно) применять свет, например, для получения теней от объектов (их можно, конечно, сделать и с помощью параллельно движущихся плоскостей, но это скорее в виде исключения). Существует три вида освещения:
5.1) THREE.AmbientLight — это освещение, которое затрагивает всю сцену. Оно не имеет направления и затрагивает каждый объект сцены в равной степени, независимо от расположения объекта. Соответственно, у этого света нет позиции на оси координат
5.2) THREE.PointLight — освещение, исходящее из одной точки во всех направлениях. Думаю, сравнение с обыкновенной лампочкой будет достаточно уместно.
5.3) THREE.DirectionalLight — свет, движущийся в определенном направлении (от заданной точки и к началу координат). Солнечный свет, например, можно отнести именно к этому типу освещения.
В примере мы будем использовать только DirectionalLight для получения тени от размещенного над плоскостью объекта.
Применяем знания
Теперь можно перейти непосредственно к коду приложения. В нем мы будем использовать все вышеописанные элементы, а также комментировать производимые действия. Для начала стартуем наше приложение посредством вызова функций инициализации и анимации, а также объявим нужные нам глобальные переменные.
//глобальные переменные
var container, camera, scene, renderer, floormesh, cubeMesh, phi = 0;
init();
animate();
Рассмотрим функцию инициализации.
function init()
{
//создаем элемент и вставляем его в тело документа
container = $( 'div' ).attr('id','cardfield');
$('body').append( container );
//создаем камеру
camera = new THREE.TrackballCamera({
fov: 45,
aspect: window.innerWidth / window.innerHeight,
near: 1,
far: 10000,
rotateSpeed: 1.0,
zoomSpeed: 1.2,
panSpeed: 0.8,
noZoom: false,
noPan: false
});
//устанавливаем камере позицию, немного разворачиваем её, чтобы она смотрела на нашу плоскость
camera.position.z = 250;
camera.position.y = 175;
camera.target.position.y = -75;
//создаем сцену
scene = new THREE.Scene();
//создаем наш "пол". Это будет псевдокуб со сторонами в 600х600 и глубиной 5
var floorgeo = new THREE.CubeGeometry(600,600,5);
//создаем мэш для него с материалом заданного цвета и прозрачностью
floormesh = new THREE.Mesh(floorgeo, new THREE.MeshBasicMaterial({color: 0x248C0F, opacity:0.9}));
//устанавливаем позицию нашему полу
floormesh.position.y = -200;
//и разворачиваем его по оси х так, чтобы он был параллелен ей.
floormesh.rotation.x = 90 * Math.PI / 180;
//добавляем к сцене
scene.addChild(floormesh);
//обвертка для куба
var materials = [
//делаем каждую сторону своего цвета
new THREE.MeshBasicMaterial( { color: 0xE01B4C }), // правая сторона
new THREE.MeshBasicMaterial( { color: 0x34609E }), // левая сторона
new THREE.MeshBasicMaterial( { color: 0x7CAD18 }), //верх
new THREE.MeshBasicMaterial( { color: 0x00EDB2 }), // низ
new THREE.MeshBasicMaterial( { color: 0xED7700 }), // лицевая сторона
new THREE.MeshBasicMaterial( { color: 0xB5B1AE }) // задняя сторона
];
//создаем куб со стороной 50 и размерами сегментов 1, применяем к нему массив материалов
var cube = new THREE.CubeGeometry( 50, 50, 50, 1, 1, 1, materials );
//создаем мэш для куба, в качестве материала мэша
//будет браться тот, который применен к кубу
cubeMesh = new THREE.Mesh( cube, new THREE.MeshFaceMaterial() );
//указываем позицию по оси y
cubeMesh.position.y = -10;
//добавляем к сцене
scene.addChild( cubeMesh );
//добавляем тень кубу
new THREE.ShadowVolume( cubeMesh );
//устанавливаем белый свет
light = new THREE.DirectionalLight( 0xffffff );
//да, объекты должны отбрасывать тень
light.castShadow = true;
//сам пол у нас в -150, свет соотв. ставим выше (в 1 по y и 0 по x и z), чтобы он попадал на наш куб и заставлял его отбрасывать тень
//напомню, что свет двигается от указанной точки к началу координат
light.position.set( 0, 1, 0 );
//добавлям свет
scene.addChild( light );
//рендерер
renderer = new THREE.WebGLRenderer();
//устанавливаем ему размеры экрана
renderer.setSize( window.innerWidth, window.innerHeight );
//и добавляем в наш созданный элемент
container.append( renderer.domElement );
}
Выполнение анимации:
function animate() {
requestAnimationFrame( animate );
render();
}
Эта функция является стандартной частью большинства приложений на three.js. Рассмотрим её поближе. requestAnimationFrame (animate) представляет собой функцию для обеспечения кроссбраузерной работы анимации (подробнее даже вот тут) и обеспечивает рекурсивный вызов функции render(), которая, собственно, и рендерит всё это наше счастье.
Рендеринг
Именно здесь нужно настраивать перемещения камеры и объектов (разумеется, заранее нужно настроить область видимости переменных таким образом, чтобы они были видны в этой функции)
function render()
{
//вращаем куб по всем трем осям (переменная мэша куба доступна глобально)
cubeMesh.rotation.x += 0.5 * Math.PI / 90;
cubeMesh.rotation.y += 1.0 * Math.PI / 90;
cubeMesh.rotation.z += 1.5 * Math.PI / 90;
//двигаем куб по кругу, изменяя координаты его позиции по осям x и y
cubeMesh.position.x = Math.sin( phi ) * 50;
cubeMesh.position.y = Math.cos( phi ) * 50;
//итерируем глобальную переменную
phi+= 0.05;
//рендерим
renderer.render(scene, camera);
}
Следует обратить внимание, что в примере использовалась билд версия three.js, взятая с github (по сравнению с обычной версией изменен вызов геометрии фигур, вместо THREE.xxx THREE.xxxGeometry, например THREE.CubeGeometry на старой версии three.js работать не будет, но будет работать THREE.Cube. Также старая версия не понимает THREE.TrackBallCamera).
Итоги:
Мы создали наше первое простенькое демо, ознакомились с базовыми компонентами, без которых невозможно создать ни одно приложение, основанное на three.js, поставили свет и увидели в действии цикл анимации. В дальнейшем мы разберемся с добавлением собственных текстур и моделей объектов, построением сложных сцен, добавлением действий над объектами сцены и переведем это все счастье на backbone.js. Исходный код приложения доступен по ссылке.
