Шпаргалка з матриць у 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. Перенесення, масштабування, одинична
Одинична
Перенесення T(tx, ty, tz)
Масштабування S(sx, sy, sz)
3. Матриці обертання
Додатний кут = обертання проти годинникової стрілки, якщо дивитися з додатної осі в напрямку до початку координат. Усі кути в радіанах.
Rx(θ) — обертання навколо осі X
Ry(θ) — обертання навколо осі Y
Rz(θ) — обертання навколо осі Z
де cθ = cos(θ), sθ = sin(θ).
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, що спричиняє перспективне ділення)
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 тощо.