Использование библиотеки three.js для построения 3D-сцен

Three.js – это библиотека JavaScript, содержащая набор готовых классов для создания и отображения интерактивной 3D графики в WebGL. В свою очередь, WebGL – это программная библиотека для JavaScript, которая позволяет создавать 3D графику, функционирующую в браузерах. Данная библиотека основана на архитектуре библиотеки OpenGL. WebGL использует язык программирования шейдеров GLSL, который имеет Си-подобный синтаксис. WebGL позволяет строить изображения непосредственно в браузере и для этого использует объект canvas.

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

Библиотека Three.js значительно упрощает работу. При использовании Three.js отпадает необходимость в написании шейдеров и появляется возможность оперировать объектами, свойственными для JavaScript. (Скачать минимизированную версию three.js)

Главным идеологом и разработчиком библиотеки является Ricardo Cobello, известный под творческим псевдонимом Mr.Doob. В то же время, в ее разработке принимают участие большое число программистов, поскольку библиотека представляет собой открытый проект.

Основными ключевыми понятиями в Three.js являются:

Scene (Сцена) – область в которой размещаются все объекты;
Camera (Камера) – направление визуализации сцены, т.е. место откуда мы наблюдаем сцену;
Renderer (Визуализация) – расчет и проецирование сцены на экран.

Объекты добавляются на сцену, а затем рендерер "фотографирует" сцену с позиции камеры.

Пространство для отрисовки (сцена)

Сцена (scene) это место, где все происходит. Когда создаются новые объекты, они добавляются в сцену, чтобы их можно было увидеть. В three.js роль сцены выполняет объект Scene. Сцену создает следующий код:

var scene = new THREE.Scene();

В дальнейшем для добавления объектов на сцену будет использоваться метод scene.add().

Проекция сцены на экран (камера)

Камера в Three.js – это объект, который определяет, с какой точки и как мы видим сцену. Без камеры рендерер ничего не покажет. Все камеры наследуются от базового класса THREE.Camera, но чаще всего используются два типа: PerspectiveCamera и OrthographicCamera.

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

Создаётся так:

const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

где параметры определяют:

  • fov (Field of View – угол обзора по вертикали, в градусах). Обычно 45–75. Больший fov – шире видно, как рыбий глаз; меньший – как телескоп.
  • aspect (соотношение сторон). Часто соотношение сторон задают, как window.innerWidth / window.innerHeight.
  • near (ближняя плоскость отсечения). Объекты ближе этого значения не отрисовываются. Обычно 0.1–1.
  • far (дальняя плоскость отсечения). Объекты дальше – не видно. Обычно 1000–10000. Слишком большой far может вызвать проблемы с точностью (z-fighting).

OrthographicCamera – ортографическая камера (параллельная проекция). Объекты одинакового размера выглядят одинаково независимо от расстояния. Полезна для 2D-игр, изометрии, CAD, схем.

Создаётся так:

const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);

где параметры определяют прямоугольный объём видимости

Основные свойства камеры (общие для всех типов)

  • position – позиция камеры в мире (Vector3). Например, camera.position.set(0, 5, 10);.
  • rotation – поворот камеры (Euler angles).
  • lookAt(vector3) – удобный метод, чтобы камера смотрела на точку: camera.lookAt(0, 0, 0);.
  • up – вектор "вверх" (по умолчанию new THREE.Vector3(0, 1, 0) – ось Y). Меняет, что считается верхом (полезно для полётов или необычных ориентаций).

Освещение сцены

Three.js предлагает несколько типов источников света. Вот самые часто используемые:

AmbientLight – равномерный фоновый свет со всех направлений. Не создаёт теней, просто "подсвечивает" всё одинаково. Полезен, чтобы избежать полностью чёрных теней.

const ambient = new THREE.AmbientLight(0xffffff, 0.5); // цвет, интенсивность
scene.add(ambient);

DirectionalLight – направленный свет, как солнце. Лучи параллельные, идут из бесконечности в заданном направлении. Отлично создаёт резкие тени.javascript

const directional = new THREE.DirectionalLight(0xffffff, 1);
directional.position.set(5, 10, 7); // направление определяется позицией
scene.add(directional);

PointLight – точечный свет, как лампочка. Излучает во все стороны, интенсивность падает с расстоянием (attenuation).

const point = new THREE.PointLight(0xff0000, 2, 100); // цвет, интенсивность, расстояние
point.position.set(0, 5, 0);
scene.add(point);

Можно добавить несколько с разными цветами для интересных эффектов.

SpotLight – имитирует прожектор или фонарик: свет идёт из точки в определённом направлении в форме конуса, с возможностью мягких краёв и теней. SpotLight сочетает свойства PointLight (свет из точки, затухание с расстоянием) и DirectionalLight (направление).

const spotLight = new THREE.SpotLight(
    0xffffff,   // цвет
    2,          // интенсивность (intensity)
    50,         // расстояние (distance) – до какого расстояния свет доходит (0 = бесконечно)
    Math.PI / 6, // угол конуса в радианах (angle) – ширина луча
    0.1,        // penumbra – мягкость краёв (0–1, где 1 = очень мягкие)
    2           // decay – как быстро затухает интенсивность с расстоянием (1 = реалистично, 2 = физически точно)
);
 
spotLight.position.set(5, 10, 5); // позиция прожектора
spotLight.target.position.set(0, 0, 0); // куда направлен (можно любой объект)
 
scene.add(spotLight);
scene.add(spotLight.target); // важно добавить target в сцену!

Ключевые параметры:

  • angle – максимальный угол конуса (в радианах). Обычно Math.PI / 4 (45°) до Math.PI / 8 (22.5°) для узкого луча. Чем меньше – тем уже луч.
  • penumbra – насколько мягкие края конуса (0 = резкие, 1 = полностью размытые).
  • decay – затухание. В старых версиях было 1 по умолчанию, теперь часто 2 для физической точности.

Размещение и визуализация объектов на сцене

Все объекты, которые будут добавлены на сцену, представляют собой сетку полигонов (см. полигональное моделирование). Для их представления и инициализации создан специальный класс:

Mesh (сетка) – это объект, который сочетает в себе форму (Geometry) и внешний вид (Material). Простыми словами: Mesh = Geometry + Material. Все видимые объекты в Three.js (кубы, сферы, модели и т.д.) – это экземпляры класса THREE.Mesh.

const geometry = new THREE.BoxGeometry(2, 2, 2); // форма: куб
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 }); // материал: зелёный, реагирует на свет
 
const mesh = new THREE.Mesh(geometry, material); // создаём Mesh
scene.add(mesh); // добавляем на сцену

Geometry – форма объекта

Geometry определяет вершины (vertices), грани (faces) и нормали (для освещения). Three.js имеет много встроенных геометрий:

  • BoxGeometry – куб
  • SphereGeometry – сфера
  • PlaneGeometry – плоскость
  • TorusGeometry – тор (бублик)
  • CylinderGeometry – цилиндр

и многие другие.

Material – внешний вид

Material определяет, как поверхность реагирует на свет, цвет, текстуры и т.д.

  • MeshStandardMaterial – физически корректный материал (PBR).
  • MeshBasicMaterial – простой цвет, не реагирует на свет (полезно для UI или glowing-объектов).
  • MeshPhongMaterial – старый, с бликами.
  • MeshLambertMaterial – матовый, без бликов.
  • MeshPhysicalMaterial – расширенный Standard с дополнительными эффектами (clearcoat, transmission).

Для получения построенной сцены на экране необходимо вызвать метод render у класса renderer = new THREE.WebGLRenderer();

var renderer = new THREE.WebGLRenderer();
renderer.render( scene, camera );

Пример кода, демонстрирующий вращение шара вокруг куба в начале статьи:

<!DOCTYPE html>
<html lang="en">
 
  <head>
    <meta charset="UTF-8">
    <title>Use Three.js</title>
    <script src="https://cgraph.ru/files/lib/three.min.js"></script>  
  </head>
 
  <body>
 
    <div width=800 height=600 id="draw"></div>
 
 
    <script>
    // ==================== СЦЕНА ====================
    // Создаём сцену — основной контейнер для всех объектов, света и камеры
    var scene = new THREE.Scene();
 
    // ==================== КАМЕРА ====================
    // Создаём перспективную камеру
    // Параметры: угол обзора (75°), соотношение сторон (800x600), ближняя плоскость (0.1), дальняя (1000)
    var camera = new THREE.PerspectiveCamera(75, 800 / 600, 0.1, 1000);
 
    // ==================== РЕНДЕРЕР ====================
    // Создаём WebGL-рендерер для отрисовки сцены
    var renderer = new THREE.WebGLRenderer();
 
    // Устанавливаем фиксированный размер canvas (800x600 пикселей)
    renderer.setSize(800, 600);
 
    // Находим контейнер на странице с id="draw" и добавляем в него canvas рендерера
    var mContainer = document.getElementById('draw');
    mContainer.appendChild(renderer.domElement);
 
    // ==================== ОСВЕЩЕНИЕ ====================
    // Передний прожектор (SpotLight) — мягкий белый свет
    var frontSpot = new THREE.SpotLight(0xeeeece);       // Цвет света — теплый белый
    frontSpot.position.set(100, 100, 100);               // Позиция прожектора в пространстве
    scene.add(frontSpot);                                // Добавляем свет на сцену
 
    // Задний прожектор — освещает с противоположной стороны для объёмности
    var backSpot = new THREE.SpotLight(0xeeeece);
    backSpot.position.set(-100, -100, -100);
    scene.add(backSpot);
 
    // ==================== КУБ ====================
    // Геометрия куба размером 10×10×10
    var geometry = new THREE.BoxGeometry(10, 10, 10);
 
    // Материал MeshBasicMaterial — простой цвет, НЕ реагирует на свет (поэтому свет на кубе не влияет)
    var material = new THREE.MeshBasicMaterial({
        color: 0xdd55ff,        // Фиолетово-розовый цвет
    });
 
    // Создаём меш (видимый объект) из геометрии и материала
    var cube = new THREE.Mesh(geometry, material);
 
    // Добавляем куб на сцену
    scene.add(cube);
 
    // ==================== СФЕРА ====================
    // Геометрия сферы: радиус 5, 20 сегментов по ширине, 20 по высоте (чем больше — тем глаже)
    var geometry = new THREE.SphereGeometry(5, 20, 20);
 
    // Материал MeshPhongMaterial — реагирует на свет, имеет блики (specular)
    var material = new THREE.MeshPhongMaterial({
        color: 0xdaa520,        // Золотисто-оранжевый цвет
        specular: 0x333333,     // Цвет блика (тёмно-серый)
        shininess: 30           // По умолчанию 30, можно изменить степень блеска
    });
 
    // Создаём сферу
    var sphere = new THREE.Mesh(geometry, material);
 
    // Смещаем сферу вправо по оси X
    sphere.position.x = 15;
 
    // Добавляем сферу на сцену
    scene.add(sphere);
 
    // ==================== НАСТРОЙКИ КАМЕРЫ И АНИМАЦИИ ====================
    var angle = 0;                  // Угол для анимации движения сферы по кругу
 
    // Отодвигаем камеру по оси Z, чтобы видеть оба объекта
    camera.position.z = 25;
 
    // ==================== ЦИКЛ АНИМАЦИИ ====================
    function render() {
        // Запрашиваем следующий кадр у браузера (обычно ~60 FPS)
        requestAnimationFrame(render);
 
        // Вращаем куб по двум осям для постоянного движения
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
 
        // Двигаем сферу по кругу в плоскости XZ (радиус 10, центр в x=15, z=0)
        sphere.position.z = 10 * Math.sin(angle);
        sphere.position.x = 15 + 10 * Math.cos(angle);  // Добавляем 15, чтобы центр круга был смещён
        angle += 0.01;                                 // Увеличиваем угол для следующего кадра
 
        // Отрисовываем сцену с текущей позиции камеры
        renderer.render(scene, camera);
    }
 
    // Запускаем анимацию
    render();
 
    </script>
  </body>
 
</html>