1. Three.js (3D-графика на WebGL)


1. Введение в трёхмерную графику на веб-страницах

Раньше для создания 3D в браузере использовались плагины (Flash, Java). Сейчас стандартом является WebGL – API для рендеринга 2D/3D-графики с использованием GPU. Работать с WebGL напрямую сложно: нужно писать шейдеры, управлять буферами, матрицами. Three.js – это библиотека, которая предоставляет удобную абстракцию над WebGL, позволяя быстро создавать 3D-сцены.

2. Основные компоненты Three.js

Любая сцена в Three.js строится из трёх ключевых элементов:

  • Сцена (Scene) – контейнер, в который мы помещаем все объекты, свет, камеры.
  • Камера (Camera) – точка зрения. Чаще всего используют PerspectiveCamera (перспективная) или OrthographicCamera (ортографическая).
  • Рендерер (Renderer) – связывает сцену и камеру, отрисовывает результат на HTML-элементе (обычно canvas).

Дополнительно используются:

  • Объекты (Mesh) – комбинация геометрии и материала.
  • Свет (Light) – источники освещения, без них объекты будут выглядеть плоскими.
  • Анимация – обновление свойств объектов в каждом кадре с помощью requestAnimationFrame.

3. Настройка окружения и первый куб

Подключение Three.js

Самый простой способ – подключить библиотеку через CDN прямо в HTML-файле:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Первый Three.js проект</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script type="importmap">
        {
            "imports": {
                "three": "https://unpkg.com/three@0.128.0/build/three.module.js"
            }
        }
    </script>
    <script type="module">
        // Здесь будет код
    </script>
</body>
</html>

Создание сцены, камеры и рендерера

import * as THREE from 'three';

// 1. Сцена
const scene = new THREE.Scene();

// 2. Камера (угол обзора, соотношение сторон, ближняя и дальняя плоскости)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);
camera.lookAt(0, 0, 0);

// 3. Рендерер
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

Добавление объекта – куба

Создаём геометрию (BoxGeometry) и материал (MeshStandardMaterial), затем объединяем в сетку (Mesh).

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x44aa88 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

Добавляем свет

Чтобы материал MeshStandardMaterial отреагировал на свет, нужен хотя бы один источник:

const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 2, 1);
scene.add(directionalLight);

Анимация и рендеринг

function animate() {
    requestAnimationFrame(animate);
    
    // Вращаем куб
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    
    renderer.render(scene, camera);
}

animate();

Теперь при открытии страницы мы увидим вращающийся куб с освещением.

4. Расширение возможностей

Разные геометрии и материалы

Three.js предоставляет множество готовых геометрий: SphereGeometry, CylinderGeometry, TorusGeometry и т.д. Материалы бывают:

  • MeshBasicMaterial – не реагирует на свет, цветной.
  • MeshStandardMaterial – физически корректный, требует света.
  • MeshPhongMaterial – более старый, но тоже поддерживает освещение.

Пример создания сферы с красным стандартным материалом:

const sphereGeo = new THREE.SphereGeometry(0.5, 32, 32);
const sphereMat = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(sphereGeo, sphereMat);
sphere.position.set(1.5, 0, 0);
scene.add(sphere);

Орбитальное управление камерой

Добавляем возможность вращать/приближать камеру мышкой с помощью OrbitControls. Для этого нужно установить или подключить соответствующий модуль:

import { OrbitControls } from 'https://unpkg.com/three@0.128.0/examples/jsm/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // плавность

Теперь можно взаимодействовать со сценой.

Текстуры

Наложение изображения на объект:

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('texture.jpg'); // путь к изображению
const materialWithTexture = new THREE.MeshStandardMaterial({ map: texture });
const texturedCube = new THREE.Mesh(geometry, materialWithTexture);
scene.add(texturedCube);

Соберём всё вместе: сцена с несколькими объектами, светом, управлением камерой и анимацией.

import * as THREE from 'three';
import { OrbitControls } from 'https://unpkg.com/three@0.128.0/examples/jsm/controls/OrbitControls.js';

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111122);

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(3, 2, 5);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Управление
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

// Объекты
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x44aa88 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

const sphereGeo = new THREE.SphereGeometry(0.7, 32, 32);
const sphereMat = new THREE.MeshStandardMaterial({ color: 0xff8844 });
const sphere = new THREE.Mesh(sphereGeo, sphereMat);
sphere.position.set(1.5, 0.5, 0);
scene.add(sphere);

// Свет
const ambientLight = new THREE.AmbientLight(0x404060);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(1, 2, 1);
scene.add(dirLight);

// Вспомогательная сетка для ориентации
const gridHelper = new THREE.GridHelper(10, 20);
scene.add(gridHelper);

function animate() {
    requestAnimationFrame(animate);
    
    cube.rotation.y += 0.01;
    sphere.rotation.x += 0.01;
    
    controls.update(); // обновляем OrbitControls
    renderer.render(scene, camera);
}

animate();

// Адаптация под изменение размера окна
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}