ФронтендРазработка игрJavaScriptHTML / CSS

Первая 3D-сцена на Three.js: куб, свет и управление мышью

Создаём сцену Three.js через Vite: камера, renderer, освещённый куб, OrbitControls, animation loop и правильный resize без искажений.

Кодик

Автор

7 мин чтения

Для первой сцены Three.js нужны scene, camera и renderer, а видимый свет требует материала, который на него реагирует. Добавим куб, пол, два источника света и OrbitControls. Затем исправим resize и animation loop, чтобы сцена не растягивалась и не тратила ресурсы через случайный setInterval.

3D-страница может быть пустой даже при правильном кубе: камера стоит внутри объекта, материал не реагирует на свет или render ни разу не вызван. В этой статье вы соберёте интерактивная 3D-сцена, которую можно вращать мышью. Финальный результат можно проверить без догадок: куб отбрасывает тень на пол, OrbitControls меняет камеру, resize сохраняет пропорции.

1Мир

Scene хранит объекты, свет и фон.

2Взгляд

PerspectiveCamera определяет точку наблюдения и перспективу.

3Кадр

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 каждый этап оставляет наблюдаемый след, поэтому следующий шаг начинается только после проверки предыдущего.

Путь Three.js от scene до canvas
От входных данных до видимого результата. Renderer соединяет мир и точку наблюдения

Не ставьте camera в точку куба. Объект и камера по умолчанию могут оказаться у начала координат. Сдвиньте camera и направьте controls.target на сцену.

Пишем куб, свет и 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 canvasPixel ratio ограничен
MeshСоединяет geometry и materialTransform ожидаемый
ControlsМеняет camera по вводуupdate вызывается в loop

Каждый новый mesh увеличивает работу CPU и GPU. Повторяющиеся объекты лучше объединять через InstancedMesh. Огромные текстуры занимают видеопамять, даже если файл сжат. Тени требуют дополнительных проходов рендера и ограниченной shadow camera. Resize не обязан вызывать setSize на каждом кадре, если размер не изменился: в production сравнивайте drawing buffer или используйте ResizeObserver. Dispose geometry, material и texture при удалении долгоживущих сцен.

Не измеряйте производительность по одному мощному ноутбуку. Проверьте сцену на встроенной графике и телефоне. Ограничьте pixel ratio, число теней и размер текстур до добавления эффектов.

Проверяем камеру, resize и animation loop

Запустите сцену и обойдите куб мышью. Уберите AmbientLight и посмотрите на тёмные стороны, затем верните его. Замените MeshStandardMaterial на MeshBasicMaterial и объясните исчезновение влияния света. Измените camera.aspect намеренно и увидьте искажение. Добавьте SphereGeometry рядом с кубом, но переиспользуйте один material. После этого включите и выключите shadowMap, сравните изображение и нагрузку.

Проверка своими руками
  1. Проверьте видимый canvas.
  2. Поверните camera вокруг target.
  3. Отключите один light.
  4. Сломайте и исправьте aspect.
  5. Добавьте второй mesh.
  6. Сравните сцену с тенями и без.
Готово, если выполняются все пункты
  • Камера видит объект.
  • Свет меняет материал.
  • Resize сохраняет форму.
  • Loop обновляет controls один раз.

Первая сцена считается понятой, когда вы можете диагностировать пустой экран без случайного копирования новых строк. После этой проверки измените одно правило Three.js и заранее запишите ожидаемый результат. Так копирование превращается в понимание.

Четыре проверки 3D-кадра
Четыре проверки перед следующим шагом. Пустой экран диагностируется по цепочке

Как развивать 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: закрыть статью, объяснить путь данных и добавить одну функцию без копирования готового блока.

Камера внутри mesh

Сдвиньте camera и проверьте near/far.

Standard material без света

Добавьте light или временно используйте BasicMaterial для диагностики.

Меняют только CSS canvas

Обновите renderer size, camera.aspect и projection matrix.

Добавляют setInterval

Используйте renderer.setAnimationLoop или requestAnimationFrame.

Сверяйтесь с первичной документацией. Команды и ограничения примера проверены по официальному руководству Three.js Creating a scene. Если версия инструмента изменилась, официальный источник важнее старого скриншота.

Что получится в итоге

Готовый результат проекта: Первая 3D-сцена на Three.js: куб, свет и управление мышью
Куб освещён, отбрасывает тень, вращается в 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, затем измените одно правило и восстановите ключевой файл без копирования.

Двигайтесь от одного mesh к маленькой сцене

JavaScript, объекты и animation loop закрепите в курсе JavaScript, а интерфейс canvas в HTML и CSS.

Сравните интерактивность с калькулятором на JavaScript, таймером обратного отсчёта и первым игровым циклом Godot 4.