Для первой сцены Three.js нужны scene, camera и renderer, а видимый свет требует материала, который на него реагирует. Добавим куб, пол, два источника света и OrbitControls. Затем исправим resize и animation loop, чтобы сцена не растягивалась и не тратила ресурсы через случайный setInterval.
3D-страница может быть пустой даже при правильном кубе: камера стоит внутри объекта, материал не реагирует на свет или render ни разу не вызван. В этой статье вы соберёте интерактивная 3D-сцена, которую можно вращать мышью. Финальный результат можно проверить без догадок: куб отбрасывает тень на пол, OrbitControls меняет камеру, resize сохраняет пропорции.
Scene хранит объекты, свет и фон.
PerspectiveCamera определяет точку наблюдения и перспективу.
WebGLRenderer рисует canvas и повторяет кадры через setAnimationLoop.
Из каких объектов состоит Three.js сцена
Scene является контейнером. Camera не рисует, а задаёт проекцию. Renderer получает оба объекта и выводит canvas. Mesh соединяет BoxGeometry и MeshStandardMaterial. OrbitControls меняет position камеры и target. Для Three.js держите этапы раздельно: ввод, проверка, основное действие и вывод. Такой маршрут позволяет остановиться в конкретной точке, увидеть фактические данные и исправить одну причину вместо случайной перестановки строк.
| Этап | Действие | Проверяемый результат |
|---|---|---|
| Создание | Scene, camera и renderer создаются один раз | Canvas добавлен в DOM |
| Объекты | Geometry и material образуют mesh | Куб находится в scene |
| Свет | Ambient и Directional освещают material | Грани имеют объём |
| Кадр | setAnimationLoop обновляет controls и render | Сцена реагирует на мышь |
Пустой экран разбирается по цепочке: canvas, камера, объект, материал, свет и render. У Three.js каждый этап оставляет наблюдаемый след, поэтому следующий шаг начинается только после проверки предыдущего.

От входных данных до видимого результата. Renderer соединяет мир и точку наблюдения
Пишем куб, свет и OrbitControls
Создайте проект npm create vite@latest three-scene -- --template vanilla, установите npm install three и запустите npm run dev. OrbitControls является addon и импортируется отдельно из three/addons/controls/OrbitControls.js. Не копируйте старые примеры с scripts/CDN вперемешку с npm-модулями.
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const canvas = document.querySelector("#scene");
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0f172a);
const camera = new THREE.PerspectiveCamera(55, 1, 0.1, 100);
camera.position.set(3, 2.4, 4.5);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1.6, 1.6, 1.6),
new THREE.MeshStandardMaterial({ color: 0x6371ff })
);
cube.position.y = 0.8;
cube.castShadow = true;
scene.add(cube);
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(12, 12),
new THREE.MeshStandardMaterial({ color: 0x334155 })
);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
scene.add(new THREE.AmbientLight(0xffffff, 0.7));
const sun = new THREE.DirectionalLight(0xffffff, 2.5);
sun.position.set(3, 6, 4);
sun.castShadow = true;
scene.add(sun);
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.target.set(0, 0.8, 0);
function resize() {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
renderer.setAnimationLoop(() => {
resize();
cube.rotation.y += 0.006;
controls.update();
renderer.render(scene, camera);
});- Canvas заполняет контейнер без растяжения.
- Куб получает объём от MeshStandardMaterial и двух lights.
- Мышь вращает и приближает camera через OrbitControls.
- Pixel ratio ограничен значением 2 для нагрузки.
Resize обновляет и размер renderer, и camera.aspect. Если изменить только CSS, изображение станет размытым или геометрически искажённым. Пример Three.js специально ограничен одним пользовательским сценарием, чтобы назначение каждой строки оставалось видимым.

Что отличает устойчивый проект от случайного запуска. Каждый объект имеет ясную роль
Как геометрия, материал и mesh соединяются
Geometry хранит вершины, material описывает поверхность, Mesh объединяет их и получает transform. PerspectiveCamera использует field of view, aspect, near и far. Объекты за плоскостями clipping не рисуются. MeshStandardMaterial участвует в физически правдоподобном освещении и нуждается в light. OrbitControls слушает события canvas и меняет camera. enableDamping требует controls.update в каждом кадре. setAnimationLoop синхронизирует render с браузером и поддерживает XR-сценарии.
| Часть | За что отвечает | Что проверить |
|---|---|---|
| Scene | Хранит граф объектов | Куб и свет добавлены |
| Camera | Проецирует 3D в экран | Aspect совпадает с canvas |
| Renderer | Рисует WebGL canvas | Pixel ratio ограничен |
| Mesh | Соединяет geometry и material | Transform ожидаемый |
| Controls | Меняет camera по вводу | update вызывается в loop |
Каждый новый mesh увеличивает работу CPU и GPU. Повторяющиеся объекты лучше объединять через InstancedMesh. Огромные текстуры занимают видеопамять, даже если файл сжат. Тени требуют дополнительных проходов рендера и ограниченной shadow camera. Resize не обязан вызывать setSize на каждом кадре, если размер не изменился: в production сравнивайте drawing buffer или используйте ResizeObserver. Dispose geometry, material и texture при удалении долгоживущих сцен.
Проверяем камеру, resize и animation loop
Запустите сцену и обойдите куб мышью. Уберите AmbientLight и посмотрите на тёмные стороны, затем верните его. Замените MeshStandardMaterial на MeshBasicMaterial и объясните исчезновение влияния света. Измените camera.aspect намеренно и увидьте искажение. Добавьте SphereGeometry рядом с кубом, но переиспользуйте один material. После этого включите и выключите shadowMap, сравните изображение и нагрузку.
- Проверьте видимый canvas.
- Поверните camera вокруг target.
- Отключите один light.
- Сломайте и исправьте aspect.
- Добавьте второй mesh.
- Сравните сцену с тенями и без.
- Камера видит объект.
- Свет меняет материал.
- Resize сохраняет форму.
- Loop обновляет controls один раз.
Первая сцена считается понятой, когда вы можете диагностировать пустой экран без случайного копирования новых строк. После этой проверки измените одно правило Three.js и заранее запишите ожидаемый результат. Так копирование превращается в понимание.

Четыре проверки перед следующим шагом. Пустой экран диагностируется по цепочке
Как развивать 3D-сцену без потери производительности
Дальше добавьте texture только после понимания UV и color space. Для загрузки моделей используйте GLTFLoader и показывайте progress. Разделите создание сцены и animation loop на функции. Добавьте GUI для света, чтобы видеть влияние параметров. Перед сложными постэффектами измерьте кадры и память. Если сцена является декоративным фоном, предусмотрите reduced motion и статичную альтернативу для слабого устройства.
Как изучить тему в Кодике
Повторите объекты, классы, события и animation loop JavaScript, затем свяжите их с координатами и векторами. В Кодике сначала восстановите чистую логику без фреймворка, затем верните библиотеку и интерфейс Three.js.
| Шаг | Что разобрать | Мини-проверка |
|---|---|---|
| 1 | Объекты JS | Создать config камеры |
| 2 | Координаты | Сдвинуть mesh по трём осям |
| 3 | События | Понять ввод OrbitControls |
| 4 | Цикл анимации | Обновить rotation |
| 5 | Ресурсы GPU | Ограничить pixel ratio |
Сначала научитесь двигать один куб и camera, потом загружайте модели и эффекты. Финальная проверка для Three.js: закрыть статью, объяснить путь данных и добавить одну функцию без копирования готового блока.
Сдвиньте camera и проверьте near/far.
Добавьте light или временно используйте BasicMaterial для диагностики.
Обновите renderer size, camera.aspect и projection matrix.
Используйте renderer.setAnimationLoop или requestAnimationFrame.
Что получится в итоге

Куб освещён, отбрасывает тень, вращается в animation loop, а камера управляется мышью без искажения resize.
Что нужно Three.js для вывода сцены?
Нужны Scene, Camera и WebGLRenderer, после чего renderer.render рисует кадр.
Почему MeshStandardMaterial может быть чёрным?
MeshStandardMaterial реагирует на освещение, поэтому сцене нужен подходящий light.
Зачем обновлять camera.aspect при resize?
Aspect должен совпадать с пропорциями canvas, иначе 3D-объекты растягиваются.
Как понять, что учебный проект Three.js действительно работает?
Проверьте основной сценарий Three.js, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.
Можно ли начать с готового кода Three.js из статьи?
Да. Сначала добейтесь результата Three.js, затем измените одно правило и восстановите ключевой файл без копирования.
JavaScript, объекты и animation loop закрепите в курсе JavaScript, а интерфейс canvas в HTML и CSS.
Сравните интерактивность с калькулятором на JavaScript, таймером обратного отсчёта и первым игровым циклом Godot 4.