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);
}