|
@@ -0,0 +1,551 @@
|
|
|
+type NumArr = number[];
|
|
|
+export const identity = () => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
|
+
|
|
|
+// 转置矩阵
|
|
|
+export const transpose = (m: number[]) => {
|
|
|
+ return [
|
|
|
+ m[0],
|
|
|
+ m[4],
|
|
|
+ m[8],
|
|
|
+ m[12],
|
|
|
+ m[1],
|
|
|
+ m[5],
|
|
|
+ m[9],
|
|
|
+ m[13],
|
|
|
+ m[2],
|
|
|
+ m[6],
|
|
|
+ m[10],
|
|
|
+ m[14],
|
|
|
+ m[3],
|
|
|
+ m[7],
|
|
|
+ m[11],
|
|
|
+ m[15],
|
|
|
+ ];
|
|
|
+};
|
|
|
+
|
|
|
+export const orthographic = (
|
|
|
+ left: number,
|
|
|
+ right: number,
|
|
|
+ bottom: number,
|
|
|
+ top: number,
|
|
|
+ near: number,
|
|
|
+ far: number
|
|
|
+) => {
|
|
|
+ const dst = new Float32Array(16);
|
|
|
+
|
|
|
+ dst[0] = 2 / (right - left);
|
|
|
+ dst[1] = 0;
|
|
|
+ dst[2] = 0;
|
|
|
+ dst[3] = 0;
|
|
|
+ dst[4] = 0;
|
|
|
+ dst[5] = 2 / (top - bottom);
|
|
|
+ dst[6] = 0;
|
|
|
+ dst[7] = 0;
|
|
|
+ dst[8] = 0;
|
|
|
+ dst[9] = 0;
|
|
|
+ dst[10] = 2 / (near - far);
|
|
|
+ dst[11] = 0;
|
|
|
+ dst[12] = (left + right) / (left - right);
|
|
|
+ dst[13] = (bottom + top) / (bottom - top);
|
|
|
+ dst[14] = (near + far) / (near - far);
|
|
|
+ dst[15] = 1;
|
|
|
+
|
|
|
+ return dst;
|
|
|
+};
|
|
|
+
|
|
|
+export const getFrustumArgumentsOnMatrix = (projectionMatrix: NumArr) => {
|
|
|
+ const inverseProjectionMatrix = inverse(projectionMatrix);
|
|
|
+ const ltn = positionTransform([-1, 1, -1], inverseProjectionMatrix);
|
|
|
+ const rbn = positionTransform([1, -1, -1], inverseProjectionMatrix);
|
|
|
+ const ccf = positionTransform([0, 0, 1], inverseProjectionMatrix);
|
|
|
+
|
|
|
+ const [left, top, near] = ltn;
|
|
|
+ const [right, bottom] = rbn;
|
|
|
+ const far = ccf[2];
|
|
|
+
|
|
|
+ return {
|
|
|
+ left,
|
|
|
+ top,
|
|
|
+ right,
|
|
|
+ bottom,
|
|
|
+ near,
|
|
|
+ far,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+export const frustum = (
|
|
|
+ left: number,
|
|
|
+ right: number,
|
|
|
+ bottom: number,
|
|
|
+ top: number,
|
|
|
+ near: number,
|
|
|
+ far: number
|
|
|
+) => {
|
|
|
+ const dst = new Float32Array(16);
|
|
|
+
|
|
|
+ var dx = right - left;
|
|
|
+ var dy = top - bottom;
|
|
|
+ var dz = far - near;
|
|
|
+
|
|
|
+ dst[0] = (2 * near) / dx;
|
|
|
+ dst[1] = 0;
|
|
|
+ dst[2] = 0;
|
|
|
+ dst[3] = 0;
|
|
|
+ dst[4] = 0;
|
|
|
+ dst[5] = (2 * near) / dy;
|
|
|
+ dst[6] = 0;
|
|
|
+ dst[7] = 0;
|
|
|
+ dst[8] = (left + right) / dx;
|
|
|
+ dst[9] = (top + bottom) / dy;
|
|
|
+ dst[10] = -(far + near) / dz;
|
|
|
+ dst[11] = -1;
|
|
|
+ dst[12] = 0;
|
|
|
+ dst[13] = 0;
|
|
|
+ dst[14] = (-2 * near * far) / dz;
|
|
|
+ dst[15] = 0;
|
|
|
+
|
|
|
+ return dst;
|
|
|
+};
|
|
|
+
|
|
|
+export const makeZToWMatrix = (fudgeFactor: number) => [
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+ fudgeFactor,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+];
|
|
|
+
|
|
|
+export const translate = (tx: number, ty: number, tz: number) => [
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ tx,
|
|
|
+ ty,
|
|
|
+ tz,
|
|
|
+ 1,
|
|
|
+];
|
|
|
+
|
|
|
+export const rotateX = (angleInRadians: number) => {
|
|
|
+ const s = Math.sin(angleInRadians);
|
|
|
+ const c = Math.cos(angleInRadians);
|
|
|
+
|
|
|
+ return [1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1];
|
|
|
+};
|
|
|
+
|
|
|
+export const rotateY = (angleInRadians: number) => {
|
|
|
+ const s = Math.sin(angleInRadians);
|
|
|
+ const c = Math.cos(angleInRadians);
|
|
|
+
|
|
|
+ return [c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1];
|
|
|
+};
|
|
|
+
|
|
|
+export const rotateZ = (angleInRadians: number) => {
|
|
|
+ const s = Math.sin(angleInRadians);
|
|
|
+ const c = Math.cos(angleInRadians);
|
|
|
+
|
|
|
+ return [c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
|
+};
|
|
|
+
|
|
|
+export const scale = (sx: number, sy: number, sz: number) => [
|
|
|
+ sx,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ sy,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ sz,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+];
|
|
|
+
|
|
|
+// 正交
|
|
|
+export const projection = (width: number, height: number, depth: number) => [
|
|
|
+ 2 / width,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ -2 / height,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 2 / depth,
|
|
|
+ 0,
|
|
|
+ -1,
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 1,
|
|
|
+];
|
|
|
+
|
|
|
+// 根据三维bound转化,正交形式
|
|
|
+export const orthogonal = (
|
|
|
+ left: number,
|
|
|
+ right: number,
|
|
|
+ top: number,
|
|
|
+ bottom: number,
|
|
|
+ near: number,
|
|
|
+ far: number
|
|
|
+) => [
|
|
|
+ 2 / (right - left),
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 2 / (bottom - top),
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 2 / (far - near),
|
|
|
+ 0,
|
|
|
+ (left + right) / (left - right),
|
|
|
+ (top + bottom) / (top - bottom),
|
|
|
+ (far + near) / (near - far),
|
|
|
+ 1,
|
|
|
+];
|
|
|
+
|
|
|
+export const perspective = (
|
|
|
+ l: number,
|
|
|
+ r: number,
|
|
|
+ t: number,
|
|
|
+ b: number,
|
|
|
+ n: number,
|
|
|
+ f: number
|
|
|
+) => [
|
|
|
+ (2 * n) / (r - l),
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ (2 * n) / (b - t),
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ (l + r) / (l - r),
|
|
|
+ (b + t) / (t - b),
|
|
|
+ 2 / (f - n),
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ -(f + n) / (n - f),
|
|
|
+ 0,
|
|
|
+];
|
|
|
+
|
|
|
+// 正中间投影 l r 和 t b对称
|
|
|
+export const straightPerspective = (w: number, h: number, n: number, f: number) => {
|
|
|
+ return [
|
|
|
+ (2 * n) / w,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ (-2 * n) / h,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 2 / (f - n),
|
|
|
+ 1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ -(f + n) / (n - f),
|
|
|
+ 0,
|
|
|
+ ];
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @param fieldOfViewInRadians 可视角度
|
|
|
+ * @param aspect w / h 比例
|
|
|
+ * @param near 近面
|
|
|
+ * @param far 远面
|
|
|
+ */
|
|
|
+export const straightPerspective1 = (
|
|
|
+ fieldOfViewInRadians: number,
|
|
|
+ aspect: number,
|
|
|
+ near: number,
|
|
|
+ far: number
|
|
|
+) => {
|
|
|
+ // const a = Math.atan((Math.PI - fieldOfViewInRadians) / 2)
|
|
|
+
|
|
|
+ // return [
|
|
|
+ // a/aspect, 0, 0, 0,
|
|
|
+ // 0, -a, 0, 0,
|
|
|
+ // 0, 0, 1/(far-near), 1,
|
|
|
+ // 0, 0, -(near)/(far-near), 0
|
|
|
+ // ]
|
|
|
+ var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
|
|
|
+ var rangeInv = 1.0 / (near - far);
|
|
|
+
|
|
|
+ return [
|
|
|
+ f / aspect,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ f,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ (near + far) * rangeInv,
|
|
|
+ -1,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ near * far * rangeInv * 2,
|
|
|
+ 0,
|
|
|
+ ];
|
|
|
+};
|
|
|
+
|
|
|
+export const multiply = (...matrixs: NumArr[]): NumArr => {
|
|
|
+ if (matrixs.length === 1) {
|
|
|
+ return matrixs[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ const radio = 4;
|
|
|
+ const count = radio * radio;
|
|
|
+ const result: number[] = [];
|
|
|
+
|
|
|
+ for (let i = 0; i < count; i++) {
|
|
|
+ const row = Math.floor(i / radio);
|
|
|
+ const column = i % radio;
|
|
|
+
|
|
|
+ let currentResult = 0;
|
|
|
+ for (let offset = 0; offset < radio; offset++) {
|
|
|
+ const rowIndex = row * radio + offset;
|
|
|
+ const columnIndex = column + offset * radio;
|
|
|
+ currentResult += matrixs[1][rowIndex] * matrixs[0][columnIndex];
|
|
|
+ }
|
|
|
+ result[i] = currentResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (matrixs.length === 2) {
|
|
|
+ return result;
|
|
|
+ } else {
|
|
|
+ return multiply(result, ...matrixs.slice(2));
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export const positionTransform = (pos: NumArr, matrix: number[]) => {
|
|
|
+ const radio = 4;
|
|
|
+ const w =
|
|
|
+ pos[0] * matrix[3] +
|
|
|
+ pos[1] * matrix[radio + 3] +
|
|
|
+ pos[2] * matrix[radio * 2 + 3] +
|
|
|
+ matrix[radio * 3 + 3];
|
|
|
+ return [
|
|
|
+ (pos[0] * matrix[0] +
|
|
|
+ pos[1] * matrix[radio] +
|
|
|
+ pos[2] * matrix[radio * 2] +
|
|
|
+ matrix[radio * 3]) /
|
|
|
+ w,
|
|
|
+ (pos[0] * matrix[1] +
|
|
|
+ pos[1] * matrix[radio + 1] +
|
|
|
+ pos[2] * matrix[radio * 2 + 1] +
|
|
|
+ matrix[radio * 3 + 1]) /
|
|
|
+ w,
|
|
|
+ (pos[0] * matrix[2] +
|
|
|
+ pos[1] * matrix[radio + 2] +
|
|
|
+ pos[2] * matrix[radio * 2 + 2] +
|
|
|
+ matrix[radio * 3 + 2]) /
|
|
|
+ w,
|
|
|
+ ];
|
|
|
+};
|
|
|
+
|
|
|
+export const addVectors = (a: NumArr, b: NumArr, v: NumArr = []) => {
|
|
|
+ v[0] = a[0] + b[0];
|
|
|
+ v[1] = a[1] + b[1];
|
|
|
+ v[2] = a[2] + b[2];
|
|
|
+ return v;
|
|
|
+};
|
|
|
+
|
|
|
+export const subtractVectors = (a: NumArr, b: NumArr) => [
|
|
|
+ a[0] - b[0],
|
|
|
+ a[1] - b[1],
|
|
|
+ a[2] - b[2],
|
|
|
+];
|
|
|
+
|
|
|
+export const scaleVector = (a: NumArr, b: number) => [a[0] * b, a[1] * b, a[2] * b];
|
|
|
+
|
|
|
+export const normalVector = (v: NumArr, cv: NumArr = []) => {
|
|
|
+ const [x, y, z] = v;
|
|
|
+ const len = Math.sqrt(x * x + y * y + z * z);
|
|
|
+ if (len > 0) {
|
|
|
+ cv[0] = x / len;
|
|
|
+ cv[1] = y / len;
|
|
|
+ cv[2] = z / len;
|
|
|
+ } else {
|
|
|
+ cv[0] = 0;
|
|
|
+ cv[1] = 0;
|
|
|
+ cv[2] = 0;
|
|
|
+ }
|
|
|
+ return cv;
|
|
|
+};
|
|
|
+
|
|
|
+// 向量叉乘,叉乘结果向量必然同事垂直两个向量
|
|
|
+export const cross = (a: NumArr, b: NumArr) => [
|
|
|
+ a[1] * b[2] - a[2] * b[1],
|
|
|
+ a[2] * b[0] - a[0] * b[2],
|
|
|
+ a[0] * b[1] - a[1] * b[0],
|
|
|
+];
|
|
|
+
|
|
|
+// 对准一个目标(实际上是制作一个矩阵,将target扭转到cameraPosition)
|
|
|
+export const lookAt = (cameraPosition: number[], target: number[], up: number[]) => {
|
|
|
+ // camera是正对-z轴的 所以需要反向
|
|
|
+ const zAxis = normalVector(subtractVectors(cameraPosition, target));
|
|
|
+ // 相对于zAxis做出x朝向,注意顺序不能反,因为三维有两个垂直朝向,通过叉乘通过顺序确认
|
|
|
+ const xAxis = normalVector(cross(up, zAxis));
|
|
|
+ const yAxis = normalVector(cross(zAxis, xAxis));
|
|
|
+
|
|
|
+ return [...xAxis, 0, ...yAxis, 0, ...zAxis, 0, ...cameraPosition, 1];
|
|
|
+};
|
|
|
+
|
|
|
+export const dot = (v1: NumArr, v2: NumArr) =>
|
|
|
+ v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
|
|
+
|
|
|
+export const inverse = (m: NumArr) => {
|
|
|
+ var m00 = m[0 * 4 + 0];
|
|
|
+ var m01 = m[0 * 4 + 1];
|
|
|
+ var m02 = m[0 * 4 + 2];
|
|
|
+ var m03 = m[0 * 4 + 3];
|
|
|
+ var m10 = m[1 * 4 + 0];
|
|
|
+ var m11 = m[1 * 4 + 1];
|
|
|
+ var m12 = m[1 * 4 + 2];
|
|
|
+ var m13 = m[1 * 4 + 3];
|
|
|
+ var m20 = m[2 * 4 + 0];
|
|
|
+ var m21 = m[2 * 4 + 1];
|
|
|
+ var m22 = m[2 * 4 + 2];
|
|
|
+ var m23 = m[2 * 4 + 3];
|
|
|
+ var m30 = m[3 * 4 + 0];
|
|
|
+ var m31 = m[3 * 4 + 1];
|
|
|
+ var m32 = m[3 * 4 + 2];
|
|
|
+ var m33 = m[3 * 4 + 3];
|
|
|
+ var tmp_0 = m22 * m33;
|
|
|
+ var tmp_1 = m32 * m23;
|
|
|
+ var tmp_2 = m12 * m33;
|
|
|
+ var tmp_3 = m32 * m13;
|
|
|
+ var tmp_4 = m12 * m23;
|
|
|
+ var tmp_5 = m22 * m13;
|
|
|
+ var tmp_6 = m02 * m33;
|
|
|
+ var tmp_7 = m32 * m03;
|
|
|
+ var tmp_8 = m02 * m23;
|
|
|
+ var tmp_9 = m22 * m03;
|
|
|
+ var tmp_10 = m02 * m13;
|
|
|
+ var tmp_11 = m12 * m03;
|
|
|
+ var tmp_12 = m20 * m31;
|
|
|
+ var tmp_13 = m30 * m21;
|
|
|
+ var tmp_14 = m10 * m31;
|
|
|
+ var tmp_15 = m30 * m11;
|
|
|
+ var tmp_16 = m10 * m21;
|
|
|
+ var tmp_17 = m20 * m11;
|
|
|
+ var tmp_18 = m00 * m31;
|
|
|
+ var tmp_19 = m30 * m01;
|
|
|
+ var tmp_20 = m00 * m21;
|
|
|
+ var tmp_21 = m20 * m01;
|
|
|
+ var tmp_22 = m00 * m11;
|
|
|
+ var tmp_23 = m10 * m01;
|
|
|
+
|
|
|
+ var t0 =
|
|
|
+ tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31 - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
|
|
|
+ var t1 =
|
|
|
+ tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31 - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
|
|
|
+ var t2 =
|
|
|
+ tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31 - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
|
|
|
+ var t3 =
|
|
|
+ tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21 - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
|
|
|
+
|
|
|
+ var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
|
|
|
+
|
|
|
+ return [
|
|
|
+ d * t0,
|
|
|
+ d * t1,
|
|
|
+ d * t2,
|
|
|
+ d * t3,
|
|
|
+ d *
|
|
|
+ (tmp_1 * m10 +
|
|
|
+ tmp_2 * m20 +
|
|
|
+ tmp_5 * m30 -
|
|
|
+ (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
|
|
|
+ d *
|
|
|
+ (tmp_0 * m00 +
|
|
|
+ tmp_7 * m20 +
|
|
|
+ tmp_8 * m30 -
|
|
|
+ (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
|
|
|
+ d *
|
|
|
+ (tmp_3 * m00 +
|
|
|
+ tmp_6 * m10 +
|
|
|
+ tmp_11 * m30 -
|
|
|
+ (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
|
|
|
+ d *
|
|
|
+ (tmp_4 * m00 +
|
|
|
+ tmp_9 * m10 +
|
|
|
+ tmp_10 * m20 -
|
|
|
+ (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
|
|
|
+ d *
|
|
|
+ (tmp_12 * m13 +
|
|
|
+ tmp_15 * m23 +
|
|
|
+ tmp_16 * m33 -
|
|
|
+ (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
|
|
|
+ d *
|
|
|
+ (tmp_13 * m03 +
|
|
|
+ tmp_18 * m23 +
|
|
|
+ tmp_21 * m33 -
|
|
|
+ (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
|
|
|
+ d *
|
|
|
+ (tmp_14 * m03 +
|
|
|
+ tmp_19 * m13 +
|
|
|
+ tmp_22 * m33 -
|
|
|
+ (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
|
|
|
+ d *
|
|
|
+ (tmp_17 * m03 +
|
|
|
+ tmp_20 * m13 +
|
|
|
+ tmp_23 * m23 -
|
|
|
+ (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
|
|
|
+ d *
|
|
|
+ (tmp_14 * m22 +
|
|
|
+ tmp_17 * m32 +
|
|
|
+ tmp_13 * m12 -
|
|
|
+ (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
|
|
|
+ d *
|
|
|
+ (tmp_20 * m32 +
|
|
|
+ tmp_12 * m02 +
|
|
|
+ tmp_19 * m22 -
|
|
|
+ (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
|
|
|
+ d *
|
|
|
+ (tmp_18 * m12 +
|
|
|
+ tmp_23 * m32 +
|
|
|
+ tmp_15 * m02 -
|
|
|
+ (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
|
|
|
+ d *
|
|
|
+ (tmp_22 * m22 +
|
|
|
+ tmp_16 * m02 +
|
|
|
+ tmp_21 * m12 -
|
|
|
+ (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)),
|
|
|
+ ];
|
|
|
+};
|