mt4.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. type NumArr = number[];
  2. export const identity = () => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
  3. // 转置矩阵
  4. export const transpose = (m: number[]) => {
  5. return [
  6. m[0],
  7. m[4],
  8. m[8],
  9. m[12],
  10. m[1],
  11. m[5],
  12. m[9],
  13. m[13],
  14. m[2],
  15. m[6],
  16. m[10],
  17. m[14],
  18. m[3],
  19. m[7],
  20. m[11],
  21. m[15],
  22. ];
  23. };
  24. export const orthographic = (
  25. left: number,
  26. right: number,
  27. bottom: number,
  28. top: number,
  29. near: number,
  30. far: number
  31. ) => {
  32. const dst = new Float32Array(16);
  33. dst[0] = 2 / (right - left);
  34. dst[1] = 0;
  35. dst[2] = 0;
  36. dst[3] = 0;
  37. dst[4] = 0;
  38. dst[5] = 2 / (top - bottom);
  39. dst[6] = 0;
  40. dst[7] = 0;
  41. dst[8] = 0;
  42. dst[9] = 0;
  43. dst[10] = 2 / (near - far);
  44. dst[11] = 0;
  45. dst[12] = (left + right) / (left - right);
  46. dst[13] = (bottom + top) / (bottom - top);
  47. dst[14] = (near + far) / (near - far);
  48. dst[15] = 1;
  49. return dst;
  50. };
  51. export const getFrustumArgumentsOnMatrix = (projectionMatrix: NumArr) => {
  52. const inverseProjectionMatrix = inverse(projectionMatrix);
  53. const ltn = positionTransform([-1, 1, -1], inverseProjectionMatrix);
  54. const rbn = positionTransform([1, -1, -1], inverseProjectionMatrix);
  55. const ccf = positionTransform([0, 0, 1], inverseProjectionMatrix);
  56. const [left, top, near] = ltn;
  57. const [right, bottom] = rbn;
  58. const far = ccf[2];
  59. return {
  60. left,
  61. top,
  62. right,
  63. bottom,
  64. near,
  65. far,
  66. };
  67. };
  68. export const frustum = (
  69. left: number,
  70. right: number,
  71. bottom: number,
  72. top: number,
  73. near: number,
  74. far: number
  75. ) => {
  76. const dst = new Float32Array(16);
  77. var dx = right - left;
  78. var dy = top - bottom;
  79. var dz = far - near;
  80. dst[0] = (2 * near) / dx;
  81. dst[1] = 0;
  82. dst[2] = 0;
  83. dst[3] = 0;
  84. dst[4] = 0;
  85. dst[5] = (2 * near) / dy;
  86. dst[6] = 0;
  87. dst[7] = 0;
  88. dst[8] = (left + right) / dx;
  89. dst[9] = (top + bottom) / dy;
  90. dst[10] = -(far + near) / dz;
  91. dst[11] = -1;
  92. dst[12] = 0;
  93. dst[13] = 0;
  94. dst[14] = (-2 * near * far) / dz;
  95. dst[15] = 0;
  96. return dst;
  97. };
  98. export const makeZToWMatrix = (fudgeFactor: number) => [
  99. 1,
  100. 0,
  101. 0,
  102. 0,
  103. 0,
  104. 1,
  105. 0,
  106. 0,
  107. 0,
  108. 0,
  109. 1,
  110. fudgeFactor,
  111. 0,
  112. 0,
  113. 0,
  114. 1,
  115. ];
  116. export const translate = (tx: number, ty: number, tz: number) => [
  117. 1,
  118. 0,
  119. 0,
  120. 0,
  121. 0,
  122. 1,
  123. 0,
  124. 0,
  125. 0,
  126. 0,
  127. 1,
  128. 0,
  129. tx,
  130. ty,
  131. tz,
  132. 1,
  133. ];
  134. export const rotateX = (angleInRadians: number) => {
  135. const s = Math.sin(angleInRadians);
  136. const c = Math.cos(angleInRadians);
  137. return [1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1];
  138. };
  139. export const rotateY = (angleInRadians: number) => {
  140. const s = Math.sin(angleInRadians);
  141. const c = Math.cos(angleInRadians);
  142. return [c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1];
  143. };
  144. export const rotateZ = (angleInRadians: number) => {
  145. const s = Math.sin(angleInRadians);
  146. const c = Math.cos(angleInRadians);
  147. return [c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
  148. };
  149. export const scale = (sx: number, sy: number, sz: number) => [
  150. sx,
  151. 0,
  152. 0,
  153. 0,
  154. 0,
  155. sy,
  156. 0,
  157. 0,
  158. 0,
  159. 0,
  160. sz,
  161. 0,
  162. 0,
  163. 0,
  164. 0,
  165. 1,
  166. ];
  167. // 正交
  168. export const projection = (width: number, height: number, depth: number) => [
  169. 2 / width,
  170. 0,
  171. 0,
  172. 0,
  173. 0,
  174. -2 / height,
  175. 0,
  176. 0,
  177. 0,
  178. 0,
  179. 2 / depth,
  180. 0,
  181. -1,
  182. 1,
  183. 0,
  184. 1,
  185. ];
  186. // 根据三维bound转化,正交形式
  187. export const orthogonal = (
  188. left: number,
  189. right: number,
  190. top: number,
  191. bottom: number,
  192. near: number,
  193. far: number
  194. ) => [
  195. 2 / (right - left),
  196. 0,
  197. 0,
  198. 0,
  199. 0,
  200. 2 / (bottom - top),
  201. 0,
  202. 0,
  203. 0,
  204. 0,
  205. 2 / (far - near),
  206. 0,
  207. (left + right) / (left - right),
  208. (top + bottom) / (top - bottom),
  209. (far + near) / (near - far),
  210. 1,
  211. ];
  212. export const perspective = (
  213. l: number,
  214. r: number,
  215. t: number,
  216. b: number,
  217. n: number,
  218. f: number
  219. ) => [
  220. (2 * n) / (r - l),
  221. 0,
  222. 0,
  223. 0,
  224. 0,
  225. (2 * n) / (b - t),
  226. 0,
  227. 0,
  228. (l + r) / (l - r),
  229. (b + t) / (t - b),
  230. 2 / (f - n),
  231. 1,
  232. 0,
  233. 0,
  234. -(f + n) / (n - f),
  235. 0,
  236. ];
  237. // 正中间投影 l r 和 t b对称
  238. export const straightPerspective = (w: number, h: number, n: number, f: number) => {
  239. return [
  240. (2 * n) / w,
  241. 0,
  242. 0,
  243. 0,
  244. 0,
  245. (-2 * n) / h,
  246. 0,
  247. 0,
  248. 0,
  249. 0,
  250. 2 / (f - n),
  251. 1,
  252. 0,
  253. 0,
  254. -(f + n) / (n - f),
  255. 0,
  256. ];
  257. };
  258. /**
  259. * @param fieldOfViewInRadians 可视角度
  260. * @param aspect w / h 比例
  261. * @param near 近面
  262. * @param far 远面
  263. */
  264. export const straightPerspective1 = (
  265. fieldOfViewInRadians: number,
  266. aspect: number,
  267. near: number,
  268. far: number
  269. ) => {
  270. // const a = Math.atan((Math.PI - fieldOfViewInRadians) / 2)
  271. // return [
  272. // a/aspect, 0, 0, 0,
  273. // 0, -a, 0, 0,
  274. // 0, 0, 1/(far-near), 1,
  275. // 0, 0, -(near)/(far-near), 0
  276. // ]
  277. var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
  278. var rangeInv = 1.0 / (near - far);
  279. return [
  280. f / aspect,
  281. 0,
  282. 0,
  283. 0,
  284. 0,
  285. f,
  286. 0,
  287. 0,
  288. 0,
  289. 0,
  290. (near + far) * rangeInv,
  291. -1,
  292. 0,
  293. 0,
  294. near * far * rangeInv * 2,
  295. 0,
  296. ];
  297. };
  298. export const multiply = (...matrixs: NumArr[]): NumArr => {
  299. if (matrixs.length === 1) {
  300. return matrixs[0];
  301. }
  302. const radio = 4;
  303. const count = radio * radio;
  304. const result: number[] = [];
  305. for (let i = 0; i < count; i++) {
  306. const row = Math.floor(i / radio);
  307. const column = i % radio;
  308. let currentResult = 0;
  309. for (let offset = 0; offset < radio; offset++) {
  310. const rowIndex = row * radio + offset;
  311. const columnIndex = column + offset * radio;
  312. currentResult += matrixs[1][rowIndex] * matrixs[0][columnIndex];
  313. }
  314. result[i] = currentResult;
  315. }
  316. if (matrixs.length === 2) {
  317. return result;
  318. } else {
  319. return multiply(result, ...matrixs.slice(2));
  320. }
  321. };
  322. export const positionTransform = (pos: NumArr, matrix: number[]) => {
  323. const radio = 4;
  324. const w =
  325. pos[0] * matrix[3] +
  326. pos[1] * matrix[radio + 3] +
  327. pos[2] * matrix[radio * 2 + 3] +
  328. matrix[radio * 3 + 3];
  329. return [
  330. (pos[0] * matrix[0] +
  331. pos[1] * matrix[radio] +
  332. pos[2] * matrix[radio * 2] +
  333. matrix[radio * 3]) /
  334. w,
  335. (pos[0] * matrix[1] +
  336. pos[1] * matrix[radio + 1] +
  337. pos[2] * matrix[radio * 2 + 1] +
  338. matrix[radio * 3 + 1]) /
  339. w,
  340. (pos[0] * matrix[2] +
  341. pos[1] * matrix[radio + 2] +
  342. pos[2] * matrix[radio * 2 + 2] +
  343. matrix[radio * 3 + 2]) /
  344. w,
  345. ];
  346. };
  347. export const addVectors = (a: NumArr, b: NumArr, v: NumArr = []) => {
  348. v[0] = a[0] + b[0];
  349. v[1] = a[1] + b[1];
  350. v[2] = a[2] + b[2];
  351. return v;
  352. };
  353. export const subtractVectors = (a: NumArr, b: NumArr) => [
  354. a[0] - b[0],
  355. a[1] - b[1],
  356. a[2] - b[2],
  357. ];
  358. export const scaleVector = (a: NumArr, b: number) => [a[0] * b, a[1] * b, a[2] * b];
  359. export const normalVector = (v: NumArr, cv: NumArr = []) => {
  360. const [x, y, z] = v;
  361. const len = Math.sqrt(x * x + y * y + z * z);
  362. if (len > 0) {
  363. cv[0] = x / len;
  364. cv[1] = y / len;
  365. cv[2] = z / len;
  366. } else {
  367. cv[0] = 0;
  368. cv[1] = 0;
  369. cv[2] = 0;
  370. }
  371. return cv;
  372. };
  373. // 向量叉乘,叉乘结果向量必然同事垂直两个向量
  374. export const cross = (a: NumArr, b: NumArr) => [
  375. a[1] * b[2] - a[2] * b[1],
  376. a[2] * b[0] - a[0] * b[2],
  377. a[0] * b[1] - a[1] * b[0],
  378. ];
  379. // 对准一个目标(实际上是制作一个矩阵,将target扭转到cameraPosition)
  380. export const lookAt = (cameraPosition: number[], target: number[], up: number[]) => {
  381. // camera是正对-z轴的 所以需要反向
  382. const zAxis = normalVector(subtractVectors(cameraPosition, target));
  383. // 相对于zAxis做出x朝向,注意顺序不能反,因为三维有两个垂直朝向,通过叉乘通过顺序确认
  384. const xAxis = normalVector(cross(up, zAxis));
  385. const yAxis = normalVector(cross(zAxis, xAxis));
  386. return [...xAxis, 0, ...yAxis, 0, ...zAxis, 0, ...cameraPosition, 1];
  387. };
  388. export const dot = (v1: NumArr, v2: NumArr) =>
  389. v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  390. export const inverse = (m: NumArr) => {
  391. var m00 = m[0 * 4 + 0];
  392. var m01 = m[0 * 4 + 1];
  393. var m02 = m[0 * 4 + 2];
  394. var m03 = m[0 * 4 + 3];
  395. var m10 = m[1 * 4 + 0];
  396. var m11 = m[1 * 4 + 1];
  397. var m12 = m[1 * 4 + 2];
  398. var m13 = m[1 * 4 + 3];
  399. var m20 = m[2 * 4 + 0];
  400. var m21 = m[2 * 4 + 1];
  401. var m22 = m[2 * 4 + 2];
  402. var m23 = m[2 * 4 + 3];
  403. var m30 = m[3 * 4 + 0];
  404. var m31 = m[3 * 4 + 1];
  405. var m32 = m[3 * 4 + 2];
  406. var m33 = m[3 * 4 + 3];
  407. var tmp_0 = m22 * m33;
  408. var tmp_1 = m32 * m23;
  409. var tmp_2 = m12 * m33;
  410. var tmp_3 = m32 * m13;
  411. var tmp_4 = m12 * m23;
  412. var tmp_5 = m22 * m13;
  413. var tmp_6 = m02 * m33;
  414. var tmp_7 = m32 * m03;
  415. var tmp_8 = m02 * m23;
  416. var tmp_9 = m22 * m03;
  417. var tmp_10 = m02 * m13;
  418. var tmp_11 = m12 * m03;
  419. var tmp_12 = m20 * m31;
  420. var tmp_13 = m30 * m21;
  421. var tmp_14 = m10 * m31;
  422. var tmp_15 = m30 * m11;
  423. var tmp_16 = m10 * m21;
  424. var tmp_17 = m20 * m11;
  425. var tmp_18 = m00 * m31;
  426. var tmp_19 = m30 * m01;
  427. var tmp_20 = m00 * m21;
  428. var tmp_21 = m20 * m01;
  429. var tmp_22 = m00 * m11;
  430. var tmp_23 = m10 * m01;
  431. var t0 =
  432. tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31 - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
  433. var t1 =
  434. tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31 - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
  435. var t2 =
  436. tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31 - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
  437. var t3 =
  438. tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21 - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
  439. var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
  440. return [
  441. d * t0,
  442. d * t1,
  443. d * t2,
  444. d * t3,
  445. d *
  446. (tmp_1 * m10 +
  447. tmp_2 * m20 +
  448. tmp_5 * m30 -
  449. (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
  450. d *
  451. (tmp_0 * m00 +
  452. tmp_7 * m20 +
  453. tmp_8 * m30 -
  454. (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
  455. d *
  456. (tmp_3 * m00 +
  457. tmp_6 * m10 +
  458. tmp_11 * m30 -
  459. (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
  460. d *
  461. (tmp_4 * m00 +
  462. tmp_9 * m10 +
  463. tmp_10 * m20 -
  464. (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
  465. d *
  466. (tmp_12 * m13 +
  467. tmp_15 * m23 +
  468. tmp_16 * m33 -
  469. (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
  470. d *
  471. (tmp_13 * m03 +
  472. tmp_18 * m23 +
  473. tmp_21 * m33 -
  474. (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
  475. d *
  476. (tmp_14 * m03 +
  477. tmp_19 * m13 +
  478. tmp_22 * m33 -
  479. (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
  480. d *
  481. (tmp_17 * m03 +
  482. tmp_20 * m13 +
  483. tmp_23 * m23 -
  484. (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
  485. d *
  486. (tmp_14 * m22 +
  487. tmp_17 * m32 +
  488. tmp_13 * m12 -
  489. (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
  490. d *
  491. (tmp_20 * m32 +
  492. tmp_12 * m02 +
  493. tmp_19 * m22 -
  494. (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
  495. d *
  496. (tmp_18 * m12 +
  497. tmp_23 * m32 +
  498. tmp_15 * m02 -
  499. (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
  500. d *
  501. (tmp_22 * m22 +
  502. tmp_16 * m02 +
  503. tmp_21 * m12 -
  504. (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)),
  505. ];
  506. };