LeCodesdocs

Mat4

Матрица 4×4 в column-major (по столбцам) — тип преобразований/проекций в SDK. Тот же контракт, что у Vec3 / Quat: все методы чистые (каждый возвращает новый Mat4; мутируют только set / copy), а везде, где принимается матрица, подойдёт и сырой массив на 16 элементов или Float32Array (Mat4Like).

В отличие от векторов, внутреннее хранилище открыто: .m — это сырой number[] в column-major (длина 16), его можно читать и писать напрямую.

Обзор

TypeScript
const world = Mat4.compose([0, 1, -3], Quat.fromEuler(0, 45, 0), 1.5)  // T · R · S
const p = world.transformPoint([0, 0, -1])          // локальная точка → мир
const { position, rotation, scale } = world.decompose()

const view = Mat4.lookAt([0, 2, 5], [0, 0, 0])      // камера в (0,2,5) смотрит в начало координат
const proj = Mat4.perspective(60 * DEG2RAD, w / h, 0.1, 100)
const mvp = proj.mul(view).mul(world)               // справа налево: world, затем view, затем proj

Сырой массив: .m

TypeScript
mat.m: number[]              // длина 16, column-major; перенос лежит в m[12..14]
mat.m[12] += vx * dt         // прямая правка чисел — .m открыт намеренно
mat.toFloat32Array()         // выгрузить копию как Float32Array для хоста/GPU

Текучие методы никогда не меняют .m — они возвращают новый Mat4, — поэтому запись в .m это единственный способ изменить матрицу на месте помимо set / copy.

Создание

TypeScript
new Mat4()                          // единичная
new Mat4(src: Mat4Like)             // копирует src (Mat4, его .m или сырой массив на 16)
Mat4.identity(): Mat4
Mat4.from(src: Mat4Like): Mat4

Mat4.compose(position, rotation, scale? /* 1 */): Mat4  // T · R · S; scale: вектор или скаляр
Mat4.fromTranslation(v): Mat4
Mat4.fromScale(v /* vector or scalar */): Mat4
Mat4.fromQuat(q): Mat4
Mat4.fromEuler(x, y, z, order? /* "YXZ" */): Mat4       // углы в ГРАДУСАХ

Композиция

TypeScript
a.mul(b): Mat4               // a · b — применяет b ПЕРВЫМ, затем a (справа налево, как в glsl)
a.premul(b): Mat4            // b · a — с другой стороны
a.invert(): Mat4             // вырожденные матрицы возвращают единичную
a.transpose(): Mat4
a.determinant(): number
a.equals(b, eps? /* 1e-6 */): boolean

Построение преобразований (локальное пространство, пост-умножение)

TypeScript
m.translate(v): Mat4              // m · T(v)
m.rotate(rad, axis): Mat4         // m · R(axis, rad) — угол в РАДИАНАХ, ось нормализуется за тебя
m.rotateX(rad)  m.rotateY(rad)  m.rotateZ(rad)
m.scale(v /* vector or scalar */): Mat4

Каждый применяет своё преобразование в локальном пространстве матрицы (пост-умножение), поэтому цепочка читается как спуск по графу сцены: Mat4.fromTranslation(pos).rotateY(a).scale(2).

Преобразование векторов

TypeScript
m.transformPoint(v): Vec3         // полное преобразование: перенос + перспективное деление
m.transformDirection(v): Vec3     // только поворот/масштаб — без переноса и деления

Vec3.transform(m) — это transformPoint со стороны вектора.

Разложение

TypeScript
m.position: Vec3                  // геттер — столбец переноса (m[12..14])
m.scaling: Vec3                   // масштаб по осям (длины базисных векторов)
m.rotation: Quat                  // поворот с вычтенным масштабом
m.eulerAngles: Vec3               // ГРАДУСЫ, порядок "YXZ"
m.toEuler(order? /* "YXZ" */): Vec3
m.decompose(): { position: Vec3, rotation: Quat, scale: Vec3 }
m.basisX / m.basisY / m.basisZ: Vec3   // локальные оси в мировом пространстве (столбцы 0/1/2)

Все геттеры возвращают свежие значения — изменение m.position не записывается обратно в матрицу (для этого пиши прямо в m.m[12..14]).

Камеры и проекция

TypeScript
Mat4.lookAt(eye, center, up? /* Vec3.up */): Mat4     // матрица ВИДА (мир → камера)
Mat4.targetTo(eye, target, up? /* Vec3.up */): Mat4   // МИРОВАЯ матрица: помещает объект в eye,
                                                      // ориентируя его лицом к target
Mat4.perspective(fovy, aspect, near, far): Mat4       // fovy в РАДИАНАХ; far может быть Infinity
Mat4.ortho(left, right, bottom, top, near, far): Mat4
Note

проекции используют clip-конвенцию WebGL/OpenGL (NDC z ∈ [−1, 1]). lookAt против targetTo — классическая ловушка: lookAt строит обратную матрицу (вида); чтобы развернуть узел к чему-то, нужен targetTo (или Quat.lookRotation).

Совместимость

TypeScript
m.set(values: ArrayLike<number>): this   // единственные мутаторы (помимо записи в .m)
m.copy(src: Mat4Like): this
m.clone(): Mat4
m.toArray(): number[]                    // копия .m
m.toFloat32Array(): Float32Array

Подводные камни

TypeScript
// ✗ градусы в rotate/perspective — аргументы-«сырые углы» это радианы
m.rotateY(90);            Mat4.perspective(60, aspect, 0.1, 100)
m.rotateY(90 * DEG2RAD);  Mat4.perspective(60 * DEG2RAD, aspect, 0.1, 100)   // ✓

// ✗ считать .m row-major — он column-major; перенос это m[12], m[13], m[14]
m.m[3] = x
m.m[12] = x   // ✓

Смотрите также

  • Vec2 и Vec3Vec3.transform(m), направления базиса.
  • QuatQuat.fromEuler / lookRotation; mat.rotation совместим с Mat4.fromQuat в обе стороны.
  • Соглашения — градусы против радианов, −Z вперёд, Y вверх.