💡 Довідник · 3D-графіка
📅 Березень 2026⏱ ~10 хв читання🟡 Середній рівень

Шпаргалка з матриць у 3D — модель, вигляд, проєкція

Усі однорідні матриці 4×4, що використовуються в конвеєрах 3D-графіки. Охоплює перенесення, обертання навколо кожної осі, масштабування, вигляд LookAt, перспективну та ортографічну проєкцію — у компонуванні по стовпцях WebGL/OpenGL із фрагментами коду Three.js.

1. Угоди: по рядках чи по стовпцях

OpenGL і WebGL зберігають матриці по стовпцях. Це означає, що 16 чисел float у Float32Array заповнюють матрицю стовпець за стовпцем. У пам’яті матриця перенесення виглядає так:

// Індекс Float32Array:  0  4  8  12
//                     1  5  9  13
//                     2  6  10 14
//                     3  7  11 15
// Перенесення T=(tx,ty,tz) по стовпцях:
const m = new Float32Array([
  1, 0, 0, 0,   // стовпець 0
  0, 1, 0, 0,   // стовпець 1
  0, 0, 1, 0,   // стовпець 2
  tx,ty,tz,1    // стовпець 3 (перенесення)
]);

Three.js внутрішньо використовує компонування по стовпцях і автоматично транспонує за потреби для GLSL. Direct3D / HLSL використовує компонування по рядках — усі матриці нижче були б транспоновані.

Коротко про угоду: У показаних на цій сторінці матрицях рядки йдуть зліва направо, а стовпці — згори донизу. Під час передавання у gl.uniformMatrix4fv() встановлюйте transpose=false, бо дані вже подані по стовпцях.

2. Перенесення, масштабування, одинична

Одинична

I
1
0
0
0
0
1
0
0
0
0
1
0
0
0
0
1

Перенесення T(tx, ty, tz)

1
0
0
0
0
1
0
0
0
0
1
0
tx
ty
tz
1

Масштабування S(sx, sy, sz)

sx
0
0
0
0
sy
0
0
0
0
sz
0
0
0
0
1

3. Матриці обертання

Додатний кут = обертання проти годинникової стрілки, якщо дивитися з додатної осі в напрямку до початку координат. Усі кути в радіанах.

Rx(θ) — обертання навколо осі X

1
0
0
0
0
0
0
−sθ
0
0
0
0
1

Ry(θ) — обертання навколо осі Y

0
−sθ
0
0
1
0
0
0
0
0
0
0
1

Rz(θ) — обертання навколо осі Z

0
0
−sθ
0
0
0
0
1
0
0
0
0
1

де cθ = cos(θ), sθ = sin(θ).

Шарнірне блокування (gimbal lock): Композиція кутів Ейлера (Rx·Ry·Rz) втрачає ступінь свободи, коли одна вісь збігається з іншою. Для плавного 3D-обертання — особливо в симуляціях — використовуйте кватерніони. Three.js Quaternion.setFromEuler() уникає шарнірного блокування.

4. Матриця вигляду / LookAt

Матриця вигляду перетворює координати світового простору у простір камери. LookAt приймає позицію ока E, цільову точку T та вектор «вгору» U:

// Три ортонормовані базисні вектори простору камери:
f = normalize(T − E)          // вперед (углиб екрана)
r = normalize(f × U)          // праворуч (U = світовий «вгору», напр. (0,1,0))
u = r × f                     // перерахований «вгору» (ортогональний)

// Матриця LookAt (по стовпцях):
// [ r.x  u.x  -f.x  0 ]
// [ r.y  u.y  -f.y  0 ]
// [ r.z  u.z  -f.z  0 ]
// [−r·E  −u·E  f·E  1 ]

Рядок перенесення кодує −dot(axis, eye), щоб зсунути все так, аби камера опинилася в початку координат простору камери.

5. Перспективна проєкція

Проєктує з простору камери (фрустум) у простір відсікання. Параметри: вертикальне поле зору fovy, співвідношення сторін a = width/height, ближня площина n, дальня площина f.

// t = tan(fovy/2) (половина висоти на одиничній відстані)
t = tan(fovy / 2)

// Перспективна матриця (по стовпцях, діапазон глибини OpenGL/WebGL −1..+1):
// [ 1/(a·t)    0        0                 0  ]
// [   0       1/t       0                 0  ]
// [   0        0   −(f+n)/(f−n)           −1 ]
// [   0        0   −2·f·n/(f−n)            0 ]
// (−1 у позиції [2][3] означає w_clip = −z_eye, що спричиняє перспективне ділення)
Точність глибини: Буфер глибини відображає [n,f] нелінійно в [−1,+1]. Точність зосереджена біля ближньої площини й розріджена біля дальньої. Для великих світів використовуйте буфер оберненої глибини (WebGPU) або логарифмічний буфер глибини (розширення Three.js), щоб уникнути z-fighting далеко від камери.

6. Ортографічна проєкція

Без перспективного ділення — паралельна проєкція. Визначається паралелепіпедом [l,r]×[b,t]×[n,f]:

// Ортографічна матриця (по стовпцях, глибина OpenGL/WebGL −1..+1):
// [ 2/(r−l)       0           0          0 ]
// [   0        2/(t−b)        0          0 ]
// [   0           0       −2/(f−n)       0 ]
// [ −(r+l)/(r−l)  −(t+b)/(t−b)  −(f+n)/(f−n)  1 ]

Використовується для 2D-HUD, елементів інтерфейсу, проходів карт тіней і CAD-видів згори. У Three.js: new THREE.OrthographicCamera(left, right, top, bottom, near, far).

7. Підсумок конвеєра MVP

// Конвеєр перетворення у вершинному шейдері:
gl_Position = uProjection * uView * uModel * aPosition;

// Еквівалентно:        P    *   V   *   M   * v_local
// Простір об’єкта  → Світовий простір (матриця моделі)
// Світовий простір → Простір камери (матриця вигляду)
// Простір камери   → Простір відсікання (матриця проєкції)
// Простір відсікання → NDC (перспективне ділення: xyz/w)
// NDC              → Пікселі екрана (перетворення в’юпорта)

Вектори нормалей потребують іншого перетворення — транспонованої оберненої матриці верхньої лівої 3×3 матриці моделі — щоб лишатися перпендикулярними до поверхонь після нерівномірного масштабування:

// У GLSL — перетворення нормалей (передається як уніформа)
// mat3 normalMatrix = transpose(inverse(mat3(uModel)));
vec3 worldNormal = normalize(uNormalMatrix * aNormal);

8. API матриць Three.js

import * as THREE from 'three';

// Будуємо матрицю моделі з TRS (рекомендовано замість ручної композиції матриць)
const object = new THREE.Object3D();
object.position.set(tx, ty, tz);
object.rotation.set(rx, ry, rz, 'XYZ');  // або використовуйте .quaternion
object.scale.set(sx, sy, sz);
object.updateMatrix();               // оновлює .matrix (матрицю моделі)
object.updateMatrixWorld(true);      // поширює на нащадків

// Прямий доступ до матриць
const model = object.matrixWorld;            // THREE.Matrix4
const view  = camera.matrixWorldInverse;     // THREE.Matrix4
const proj  = camera.projectionMatrix;       // THREE.Matrix4

// Ручні операції з матрицями
const m = new THREE.Matrix4();
m.makeTranslation(1, 2, 3);
m.makeRotationY(Math.PI / 4);
m.makeScale(2, 2, 2);

// Композиція з TRS
m.compose(position, quaternion, scale);

// Витягуємо у Float32Array для уніформ WebGL
const arr = new Float32Array(16);
model.toArray(arr);
gl.uniformMatrix4fv(loc, false, arr);
Уникайте ручного обернення в шейдерах: Обчислення inverse(mat4) у GLSL витратне (20+ операцій). Обчислюйте обернені матриці заздалегідь на CPU (де доступний float64) і передавайте їх як уніформи: uModelInvTranspose, uViewInv тощо.