Довідник

Швидкий довідник API Three.js

Довідник для копіювання коду з тим, що ви використовуєте щодня в Three.js (r160): налаштування рендерера й сцени, геометрії, матеріали, освітлення, граф сцени, лоадери, цикл анімації та рейкастинг.

Основа: рендерер, сцена, камера

Кожному застосунку на Three.js потрібні рівно три об'єкти верхнього рівня: WebGLRenderer, який малює пікселі, Scene, що містить граф сцени, і Camera, що визначає точку огляду. Налаштуйте їх один раз і повторно використовуйте протягом усього життя застосунку.

import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';

// Рендерер
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);

// Сцена
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a0a);
scene.fog = new THREE.Fog(0x0a0a0a, 10, 60);

// Перспективна камера: fov, aspect, near, far
const camera = new THREE.PerspectiveCamera(
  60,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);

// Ортографічна камера (для 2D-стилю / ізометричних видів)
const frustumSize = 10;
const aspect = window.innerWidth / window.innerHeight;
const orthoCam = new THREE.OrthographicCamera(
  (-frustumSize * aspect) / 2, (frustumSize * aspect) / 2,
  frustumSize / 2, -frustumSize / 2,
  0.1, 1000
);
Властивість рендерера Призначення
setPixelRatio() Відповідність DPR пристрою; обмежте до 2, щоб не перевантажувати GPU на retina-екранах
outputColorSpace THREE.SRGBColorSpace для коректної гами на фінальному виводі
toneMapping ACESFilmic або Reinhard для HDR-освітлених PBR-сцен
shadowMap.type PCFSoftShadowMap для м'яких країв; BasicShadowMap — найдешевший
setClearColor(color, alpha) Колір фону, коли scene.background не задано

Геометрії

Геометрії — це екземпляри BufferGeometry, що описують позиції вершин, нормалі та UV-координати. Вбудовані конструктори покривають найпоширеніші примітиви.

Конструктор Основні аргументи
BoxGeometry width, height, depth, widthSeg, heightSeg, depthSeg
SphereGeometry radius, widthSegments, heightSegments
PlaneGeometry width, height, widthSeg, heightSeg
CylinderGeometry radiusTop, radiusBottom, height, radialSegments
TorusGeometry radius, tube, radialSegments, tubularSegments
ConeGeometry radius, height, radialSegments
IcosahedronGeometry radius, detail (0 = плоский low-poly)
ExtrudeGeometry Shape + { depth, bevelEnabled, steps }
// Власна BufferGeometry з сирих даних вершин
const geo = new THREE.BufferGeometry();
const positions = new Float32Array([
  -1, -1, 0,
   1, -1, 0,
   0,  1, 0,
]);
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.computeVertexNormals();
geo.computeBoundingSphere(); // потрібно для коректного frustum culling

// Об'єднання кількох геометрій в один draw call
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const merged = mergeGeometries([geoA, geoB, geoC]);

Матеріали

Матеріали визначають, як поверхня реагує на світло. MeshStandardMaterial та MeshPhysicalMaterial є фізично коректними (PBR) і є стандартним вибором для реалістичного освітлення.

Матеріал Модель освітлення Застосування
MeshBasicMaterial Без освітлення (unlit) UI-накладки, каркаси, суто емісивні фігури
MeshLambertMaterial Лише дифузне Дешеві матові поверхні, мобільні пристрої
MeshPhongMaterial Дифузне + дзеркальне Блискучий пластик без повної вартості PBR
MeshStandardMaterial PBR metallic/roughness Стандарт для реалістичних сцен
MeshPhysicalMaterial PBR + clearcoat/transmission Скло, автомобільна фарба, покриті поверхні
ShaderMaterial Власний GLSL Повний контроль — власні vertex/fragment шейдери
const mat = new THREE.MeshStandardMaterial({
  color: 0x3388ff,
  metalness: 0.2,
  roughness: 0.6,
  map: colorTexture,          // альбедо
  normalMap: normalTexture,
  roughnessMap: roughTexture,
  envMapIntensity: 1.0,
  transparent: true,
  opacity: 0.9,
  side: THREE.DoubleSide,     // FrontSide | BackSide | DoubleSide
});

// Власний шейдерний матеріал
const shaderMat = new THREE.ShaderMaterial({
  uniforms: {
    uTime:  { value: 0 },
    uColor: { value: new THREE.Color(0xff5500) },
  },
  vertexShader: /* glsl */ `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: /* glsl */ `
    uniform float uTime;
    uniform vec3 uColor;
    varying vec2 vUv;
    void main() {
      gl_FragColor = vec4(uColor * (0.5 + 0.5 * sin(uTime + vUv.x * 6.28)), 1.0);
    }
  `,
});

Освітлення

Джерело світла Поведінка Вартість
AmbientLight Рівномірне світло без напрямку, без тіней Безкоштовно
HemisphereLight Градієнт небо/земля — дешеве наближення зовнішнього освітлення Безкоштовно
DirectionalLight Паралельні промені (сонце); тіні через ортографічний frustum Дешево
PointLight Випромінює з точки; тінь використовує cube map (6 проходів) Дорого з тінями
SpotLight Конус світла з кутом і напівтінню (penumbra) Помірно
RectAreaLight М'яке світло від прямокутної панелі (без тіней) Помірно, потребує ініціалізації LTC-текстур
const ambient = new THREE.AmbientLight(0xffffff, 0.4);

const sun = new THREE.DirectionalLight(0xffffff, 3);
sun.position.set(5, 10, 5);
sun.castShadow = true;
sun.shadow.mapSize.set(2048, 2048);
sun.shadow.camera.left   = -10;
sun.shadow.camera.right  =  10;
sun.shadow.camera.top    =  10;
sun.shadow.camera.bottom = -10;
sun.shadow.bias = -0.0005; // зменшує shadow acne

const spot = new THREE.SpotLight(0xffaa33, 5, 20, Math.PI / 6, 0.3);
spot.position.set(0, 8, 0);

scene.add(ambient, sun, spot);
Лише DirectionalLight, PointLight та SpotLight можуть відкидати тіні. Кожен меш, який має відкидати або приймати тінь, потребує явного встановлення mesh.castShadow = true / mesh.receiveShadow = true — самого лише shadowMap.enabled на рендерері недостатньо.

Об'єкти та граф сцени

Object3D — базовий клас для всього, що можна розмістити в сцені: меші, групи, камери й джерела світла успадковують його трансформацію (позицію, обертання/кватерніон, масштаб) та можливість вкладеності (parenting).

const mesh = new THREE.Mesh(geo, mat);
mesh.position.set(0, 1, 0);
mesh.rotation.set(0, Math.PI / 4, 0);   // Euler, радіани
mesh.scale.setScalar(1.5);
mesh.castShadow = true;
mesh.receiveShadow = true;

// Групування об'єктів для спільної трансформації
const group = new THREE.Group();
group.add(mesh, otherMesh);
group.position.y = 2;
scene.add(group);

// Обхід усього графа
scene.traverse((obj) => {
  if (obj.isMesh) obj.material.needsUpdate = true;
});

// Трансформації у світових координатах (з урахуванням батьків)
const worldPos = new THREE.Vector3();
mesh.getWorldPosition(worldPos);

// InstancedMesh для тисяч однакових об'єктів в одному draw call
const instanced = new THREE.InstancedMesh(geo, mat, 5000);
const m4 = new THREE.Matrix4();
for (let i = 0; i < 5000; i++) {
  m4.makeTranslation(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);
  instanced.setMatrixAt(i, m4);
}
instanced.instanceMatrix.needsUpdate = true;
scene.add(instanced);

Контроли

Контрол Типове застосування
OrbitControls Обертання/масштабування/панорама навколо цілі — стандарт для демо й редакторів
PointerLockControls Огляд від першої особи мишею, блокує курсор
TrackballControls Вільне обертання без фіксованого вектора "вгору"
TransformControls Гізмо для переміщення/обертання/масштабування у редакторах
FlyControls Камера вільного польоту (у стилі космічного корабля)
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 2;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2; // не дозволяє камері опускатися під землю

// Потрібно викликати щокадру, коли увімкнено демпфування (damping)
function tick() {
  controls.update();
  renderer.render(scene, camera);
}

Лоадери

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

// GLTF/GLB моделі, опційно стиснуті Draco
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');

const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load('model.glb', (gltf) => {
  scene.add(gltf.scene);
  gltf.animations; // AnimationClip[]
});

// Текстури
const texLoader = new THREE.TextureLoader();
const colorMap = texLoader.load('albedo.jpg');
colorMap.colorSpace = THREE.SRGBColorSpace; // кольорові текстури потребують sRGB
colorMap.wrapS = colorMap.wrapT = THREE.RepeatWrapping;
colorMap.anisotropy = renderer.capabilities.getMaxAnisotropy();

// HDR карти оточення
new RGBELoader().load('studio.hdr', (hdrTexture) => {
  hdrTexture.mapping = THREE.EquirectangularReflectionMapping;
  scene.environment = hdrTexture; // image-based lighting для PBR-матеріалів
  scene.background = hdrTexture;
});
Усі лоадери асинхронні й побудовані на колбеках або Promise. Використовуйте LoadingManager, щоб відстежувати загальний прогрес завантаження багатьох ресурсів, і завжди обробляйте колбек onError — невдале завантаження ресурсу має деградувати без падіння всієї сцени.

Цикл анімації

const clock = new THREE.Clock();

function tick() {
  const dt = clock.getDelta();       // секунди з попереднього кадру
  const elapsed = clock.getElapsedTime();

  mesh.rotation.y += dt * 0.5;
  shaderMat.uniforms.uTime.value = elapsed;
  controls.update();

  renderer.render(scene, camera);
}

// Сумісний з WebXR цикл анімації (кращий за requestAnimationFrame)
renderer.setAnimationLoop(tick);

// Зупинити цикл
// renderer.setAnimationLoop(null);

// Обробка зміни розміру
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

Рейкастинг і взаємодія

Raycaster пускає промінь від камери через нормалізовану позицію вказівника й повідомляє про перетини — стандартний спосіб клікати, наводити курсор або обирати 3D-об'єкти.

const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

window.addEventListener('pointermove', (event) => {
  // Перетворення в нормалізовані координати пристрою: від -1 до +1
  pointer.x =  (event.clientX / window.innerWidth)  * 2 - 1;
  pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

window.addEventListener('click', () => {
  raycaster.setFromCamera(pointer, camera);

  // true = перевіряти нащадків рекурсивно (потрібно для груп)
  const hits = raycaster.intersectObjects(scene.children, true);

  if (hits.length > 0) {
    const { object, point, distance, face } = hits[0];
    object.material.color.set(0xff0000);
    console.log('Точка перетину:', point, 'відстань:', distance);
  }
});
Рейкастинг проти тисяч об'єктів щокадру — витратна операція. Обмежте список кандидатів (передавайте конкретний масив, а не scene.children), або використовуйте просторовий індекс (octree/BVH через three-mesh-bvh) для великих статичних сцен.

Математичні типи

Тип Поширені методи
Vector3 .set(), .add(), .sub(), .multiplyScalar(), .normalize(), .length(), .lerp(), .cross(), .dot(), .applyQuaternion()
Quaternion .setFromAxisAngle(), .setFromEuler(), .slerp(), .multiply(), .invert()
Euler x, y, z (радіани) + порядок обертання, напр. 'XYZ'
Matrix4 .compose(), .decompose(), .makeTranslation(), .multiplyMatrices(), .invert()
Box3 .setFromObject(), .containsPoint(), .intersectsBox(), .getCenter()
MathUtils .degToRad(), .radToDeg(), .clamp(), .lerp(), .randFloatSpread(), .smoothstep()
// Усі тимчасові об'єкти варто виносити за межі гарячих циклів, щоб уникнути навантаження на GC
const _v = new THREE.Vector3();
const _q = new THREE.Quaternion();

_q.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
_v.set(1, 0, 0).applyQuaternion(_q); // обертання вектора кватерніоном

// Розкладання світової матриці на позицію/кватерніон/масштаб
const pos = new THREE.Vector3();
const quat = new THREE.Quaternion();
const scale = new THREE.Vector3();
mesh.matrixWorld.decompose(pos, quat, scale);
Для інтерполяції та композиції обертань надавайте перевагу кватерніонам, а не кутам Ейлера — кути Ейлера страждають від gimbal lock і не інтерполюються лінійно найкоротшим шляхом. Використовуйте Euler лише для створення/читання зрозумілих людині обертань.