Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  1. /**
  2. * @file MARTINS.js WebAR demo with WebGL
  3. * @version 1.1.0
  4. * @author Alexandre Martins (https://github.com/alemart)
  5. * @license LGPL-3.0-or-later
  6. */
  7. /*
  8. WebGL low-level code:
  9. */
  10. class Entity
  11. {
  12. constructor(gl)
  13. {
  14. this._gl = gl;
  15. this._program = this._compile(gl, this.vertexShader, this.fragmentShader);
  16. this._uniformLocation = {
  17. 'time': gl.getUniformLocation(this._program, 'time'),
  18. 'resolution': gl.getUniformLocation(this._program, 'resolution'),
  19. 'projectionMatrix': gl.getUniformLocation(this._program, 'projectionMatrix'),
  20. 'modelViewMatrix': gl.getUniformLocation(this._program, 'modelViewMatrix'),
  21. };
  22. this.projectionMatrix = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
  23. this.modelViewMatrix = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
  24. }
  25. render(time = 0)
  26. {
  27. const gl = this._gl, u = this._uniformLocation;
  28. gl.useProgram(this._program);
  29. gl.uniform1f(u.time, time);
  30. gl.uniform2f(u.resolution, gl.canvas.width, gl.canvas.height);
  31. gl.uniformMatrix4fv(u.projectionMatrix, false, this.projectionMatrix);
  32. gl.uniformMatrix4fv(u.modelViewMatrix, false, this.modelViewMatrix);
  33. gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  34. this._render(time);
  35. }
  36. _render(time)
  37. {
  38. const gl = this._gl;
  39. gl.drawArrays(gl.TRIANGLES, 0, 3 * this.numberOfTriangles);
  40. }
  41. get numberOfTriangles()
  42. {
  43. throw new Error();
  44. }
  45. get vertexShader()
  46. {
  47. throw new Error();
  48. }
  49. get fragmentShader()
  50. {
  51. throw new Error();
  52. }
  53. get _includeShaderUtils()
  54. {
  55. return `
  56. mat4 translate(vec3 offset)
  57. {
  58. float x = offset.x, y = offset.y, z = offset.z;
  59. return mat4(
  60. 1, 0, 0, 0,
  61. 0, 1, 0, 0,
  62. 0, 0, 1, 0,
  63. x, y, z, 1
  64. );
  65. }
  66. mat4 scale(vec3 factor)
  67. {
  68. float x = factor.x, y = factor.y, z = factor.z;
  69. return mat4(
  70. x, 0, 0, 0,
  71. 0, y, 0, 0,
  72. 0, 0, z, 0,
  73. 0, 0, 0, 1
  74. );
  75. }
  76. mat4 rotateX(float rad)
  77. {
  78. float s = sin(rad), c = cos(rad);
  79. return mat4(
  80. 1, 0, 0, 0,
  81. 0, c, s, 0,
  82. 0,-s, c, 0,
  83. 0, 0, 0, 1
  84. );
  85. }
  86. mat4 rotateY(float rad)
  87. {
  88. float s = sin(rad), c = cos(rad);
  89. return mat4(
  90. c, 0,-s, 0,
  91. 0, 1, 0, 0,
  92. s, 0, c, 0,
  93. 0, 0, 0, 1
  94. );
  95. }
  96. mat4 rotateZ(float rad)
  97. {
  98. float s = sin(rad), c = cos(rad);
  99. return mat4(
  100. c, s, 0, 0,
  101. -s, c, 0, 0,
  102. 0, 0, 1, 0,
  103. 0, 0, 0, 1
  104. );
  105. }
  106. mat4 rotate(vec3 euler)
  107. {
  108. return rotateZ(euler.z) * rotateY(euler.y) * rotateX(euler.x);
  109. }
  110. `;
  111. }
  112. get _includeVertexShaderUtils()
  113. {
  114. return this._includeShaderUtils + this._transform;
  115. }
  116. get _includeFragmentShaderUtils()
  117. {
  118. return this._includeShaderUtils + this._colorize;
  119. }
  120. get _transform()
  121. {
  122. return `
  123. vec4 transform(vec4 v)
  124. {
  125. return v;
  126. }
  127. `;
  128. }
  129. get _colorize()
  130. {
  131. return `
  132. vec4 colorize(vec4 p)
  133. {
  134. return p;
  135. }
  136. `;
  137. }
  138. _createShader(gl, shaderType, shaderSource)
  139. {
  140. const shader = gl.createShader(shaderType);
  141. gl.shaderSource(shader, shaderSource);
  142. gl.compileShader(shader);
  143. if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  144. const message = gl.getShaderInfoLog(shader);
  145. gl.deleteShader(shader);
  146. throw new Error(message);
  147. }
  148. return shader;
  149. }
  150. _createProgram(gl, vs, fs)
  151. {
  152. const program = gl.createProgram();
  153. gl.attachShader(program, vs);
  154. gl.attachShader(program, fs);
  155. gl.linkProgram(program);
  156. if(!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  157. const message = gl.getProgramInfoLog(program);
  158. gl.deleteProgram(program);
  159. throw new Error(message);
  160. }
  161. return program;
  162. }
  163. _compile(gl, vsSource, fsSource)
  164. {
  165. const vs = this._createShader(gl, gl.VERTEX_SHADER, vsSource);
  166. const fs = this._createShader(gl, gl.FRAGMENT_SHADER, fsSource);
  167. const program = this._createProgram(gl, vs, fs);
  168. return program;
  169. }
  170. }
  171. class Pyramid extends Entity
  172. {
  173. get numberOfTriangles()
  174. {
  175. return 4;
  176. }
  177. get vertexShader()
  178. {
  179. return `#version 300 es
  180. uniform mat4 projectionMatrix;
  181. uniform mat4 modelViewMatrix;
  182. uniform float time;
  183. uniform vec2 resolution;
  184. flat out int triangleID;
  185. out vec2 control;
  186. const float VERTEX[] = float[](
  187. 0.5, -0.25, 0.25,
  188. 0.0, 0.25, 0.0,
  189. -0.5, -0.25, 0.25,
  190. -0.5, -0.25, 0.25,
  191. 0.0, 0.25, 0.0,
  192. 0.0, -0.25, -0.5,
  193. 0.0, -0.25, -0.5,
  194. 0.0, 0.25, 0.0,
  195. 0.5, -0.25, 0.25,
  196. 0.0, -0.25, -0.5,
  197. 0.5, -0.25, 0.25,
  198. -0.5, -0.25, 0.25
  199. );
  200. const vec3 CENTROID = vec3(
  201. 0.0, -0.125, 0.0
  202. );
  203. const vec2 UV_CONTROL_POINT[] = vec2[](
  204. vec2(0.0, 1.0),
  205. vec2(1.0, -1.0),
  206. vec2(-1.0, -1.0)
  207. );
  208. ${this._includeVertexShaderUtils}
  209. void main()
  210. {
  211. int base = gl_VertexID * 3;
  212. vec4 vertex = vec4(VERTEX[base], VERTEX[base+1], VERTEX[base+2], 1.0);
  213. gl_Position = projectionMatrix * modelViewMatrix * transform(vertex);
  214. triangleID = gl_VertexID / 3;
  215. control = UV_CONTROL_POINT[gl_VertexID % 3];
  216. }
  217. `;
  218. }
  219. get fragmentShader()
  220. {
  221. return `#version 300 es
  222. precision mediump float;
  223. flat in int triangleID;
  224. out vec4 color;
  225. const vec3 COLOR[] = vec3[](
  226. vec3(0,1,1),
  227. vec3(1,1,0),
  228. vec3(1,0,1),
  229. vec3(0,1,0)
  230. );
  231. ${this._includeFragmentShaderUtils}
  232. void main()
  233. {
  234. vec4 pixel = vec4(COLOR[triangleID], 1.0);
  235. color = colorize(pixel);
  236. }
  237. `;
  238. }
  239. get _colorize()
  240. {
  241. // create black borders
  242. return `
  243. const float THICKNESS = 0.05;
  244. in vec2 control;
  245. vec4 colorize(vec4 p)
  246. {
  247. const float SQRT5 = float(${Math.sqrt(5.0)});
  248. float x = control.x, y = control.y;
  249. float d = abs(y + 1.0);
  250. d = min(d, abs(2.0 * x - y + 1.0) / SQRT5);
  251. d = min(d, abs(-2.0 * x - y + 1.0) / SQRT5);
  252. p.rgb *= step(THICKNESS, d);
  253. return p;
  254. }
  255. `;
  256. }
  257. }
  258. class AnimatedPyramid extends Pyramid
  259. {
  260. get _transform()
  261. {
  262. // rotate around the centroid
  263. return `
  264. const float CYCLE_DURATION = 3.0;
  265. const vec3 POSITION = vec3(0.0, 0.0, 0.5);
  266. const vec3 SCALE = vec3(1.5);
  267. vec4 transform(vec4 v)
  268. {
  269. const float PI = float(${Math.PI});
  270. float rad = -(2.0 * PI / CYCLE_DURATION) * time;
  271. v = translate(-CENTROID) * v;
  272. v = rotate(vec3(rad, rad, 0.0)) * v;
  273. v = translate(CENTROID) * v;
  274. v = scale(SCALE) * v;
  275. v = translate(POSITION) * v;
  276. return v;
  277. }
  278. `;
  279. }
  280. }
  281. class Cube extends Entity
  282. {
  283. get numberOfTriangles()
  284. {
  285. return 12; // 6 faces * 2 triangles per face
  286. }
  287. get vertexShader()
  288. {
  289. return `#version 300 es
  290. uniform mat4 projectionMatrix;
  291. uniform mat4 modelViewMatrix;
  292. uniform float time;
  293. uniform vec2 resolution;
  294. flat out int faceID;
  295. out vec2 control;
  296. const float VERTEX[] = float[](
  297. 1.0, 1.0, 1.0,
  298. -1.0, 1.0, 1.0,
  299. -1.0, -1.0, 1.0,
  300. -1.0, -1.0, 1.0,
  301. 1.0, -1.0, 1.0,
  302. 1.0, 1.0, 1.0,
  303. 1.0, 1.0, -1.0,
  304. -1.0, 1.0, -1.0,
  305. -1.0, -1.0, -1.0,
  306. -1.0, -1.0, -1.0,
  307. 1.0, -1.0, -1.0,
  308. 1.0, 1.0, -1.0,
  309. 1.0, 1.0, 1.0,
  310. -1.0, 1.0, 1.0,
  311. -1.0, 1.0, -1.0,
  312. -1.0, 1.0, -1.0,
  313. 1.0, 1.0, -1.0,
  314. 1.0, 1.0, 1.0,
  315. 1.0, -1.0, 1.0,
  316. -1.0, -1.0, 1.0,
  317. -1.0, -1.0, -1.0,
  318. -1.0, -1.0, -1.0,
  319. 1.0, -1.0, -1.0,
  320. 1.0, -1.0, 1.0,
  321. 1.0, 1.0, 1.0,
  322. 1.0, -1.0, 1.0,
  323. 1.0, -1.0, -1.0,
  324. 1.0, -1.0, -1.0,
  325. 1.0, 1.0, -1.0,
  326. 1.0, 1.0, 1.0,
  327. -1.0, 1.0, 1.0,
  328. -1.0, -1.0, 1.0,
  329. -1.0, -1.0, -1.0,
  330. -1.0, -1.0, -1.0,
  331. -1.0, 1.0, -1.0,
  332. -1.0, 1.0, 1.0
  333. );
  334. const vec3 CENTROID = vec3(
  335. 0.0, 0.0, 0.0
  336. );
  337. const vec2 UV_CONTROL_POINT[] = vec2[](
  338. vec2(1.0, 1.0),
  339. vec2(0.0, 1.0),
  340. vec2(0.0, 0.0),
  341. vec2(0.0, 0.0),
  342. vec2(1.0, 0.0),
  343. vec2(1.0, 1.0)
  344. );
  345. ${this._includeVertexShaderUtils}
  346. void main()
  347. {
  348. int base = gl_VertexID * 3;
  349. vec4 vertex = vec4(VERTEX[base], VERTEX[base+1], VERTEX[base+2], 1.0);
  350. gl_Position = projectionMatrix * modelViewMatrix * transform(vertex);
  351. faceID = gl_VertexID / 6;
  352. control = UV_CONTROL_POINT[gl_VertexID % 6];
  353. }
  354. `;
  355. }
  356. get fragmentShader()
  357. {
  358. return `#version 300 es
  359. precision mediump float;
  360. flat in int faceID;
  361. out vec4 color;
  362. const vec3 COLOR[] = vec3[](
  363. vec3(1,0,1),
  364. vec3(1,1,0),
  365. vec3(0,1,1),
  366. vec3(1,0,0),
  367. vec3(0,0,1),
  368. vec3(0,1,0)
  369. );
  370. ${this._includeFragmentShaderUtils}
  371. void main()
  372. {
  373. vec4 pixel = vec4(COLOR[faceID], 1.0);
  374. color = colorize(pixel);
  375. }
  376. `;
  377. }
  378. get _colorize()
  379. {
  380. // create black borders
  381. return `
  382. const float THICKNESS = 0.03;
  383. in vec2 control;
  384. vec4 colorize(vec4 p)
  385. {
  386. float x = control.x, y = control.y;
  387. vec4 d4 = abs(vec4(x, y, x - 1.0, y - 1.0));
  388. vec2 d2 = min(d4.xy, d4.zw);
  389. float d = min(d2.x, d2.y);
  390. p.rgb *= step(THICKNESS, d);
  391. return p;
  392. }
  393. `;
  394. }
  395. }
  396. class AnimatedCube extends Cube
  397. {
  398. get _transform()
  399. {
  400. // rotate around the centroid
  401. return `
  402. const float CYCLE_DURATION = 3.0;
  403. const vec3 POSITION = vec3(-0.5, -1.5, 2.0);
  404. const vec3 SCALE = vec3(0.5);
  405. vec4 transform(vec4 v)
  406. {
  407. const float PI = float(${Math.PI});
  408. float rad = -(2.0 * PI / CYCLE_DURATION) * time;
  409. v = translate(-CENTROID) * v;
  410. v = rotateY(rad) * v;
  411. v = translate(CENTROID) * v;
  412. v = scale(SCALE) * v;
  413. v = translate(POSITION) * v;
  414. return v;
  415. }
  416. `;
  417. }
  418. }
  419. class Quad extends Entity
  420. {
  421. get numberOfTriangles()
  422. {
  423. return 2;
  424. }
  425. get vertexShader()
  426. {
  427. return `#version 300 es
  428. uniform mat4 projectionMatrix;
  429. uniform mat4 modelViewMatrix;
  430. uniform float time;
  431. uniform vec2 resolution;
  432. out vec2 texCoord;
  433. const float UV[] = float[](
  434. 0.0, 0.0,
  435. 1.0, 0.0,
  436. 0.0, 1.0,
  437. 0.0, 1.0,
  438. 1.0, 0.0,
  439. 1.0, 1.0
  440. );
  441. const float QUAD[] = float[](
  442. -1.0,-1.0,
  443. 1.0,-1.0,
  444. -1.0, 1.0,
  445. -1.0, 1.0,
  446. 1.0,-1.0,
  447. 1.0, 1.0
  448. );
  449. ${this._includeVertexShaderUtils}
  450. ${this._rectify}
  451. ${this._rectifyUV}
  452. void main()
  453. {
  454. int base = gl_VertexID * 2;
  455. vec4 vertex = vec4(QUAD[base], QUAD[base+1], 0.0, 1.0);
  456. vec4 v = rectify(vertex);
  457. gl_Position = projectionMatrix * modelViewMatrix * transform(v);
  458. vec2 uv = vec2(UV[base], UV[base+1]) * vec2(1,-1);
  459. texCoord = rectifyUV(uv);
  460. }
  461. `;
  462. }
  463. get _rectify()
  464. {
  465. return `
  466. vec4 rectify(vec4 v)
  467. {
  468. return v;
  469. }
  470. `;
  471. }
  472. get _rectifyUV()
  473. {
  474. return `
  475. vec2 rectifyUV(vec2 v)
  476. {
  477. return v;
  478. }
  479. `;
  480. }
  481. }
  482. class ImageQuad extends Quad
  483. {
  484. constructor(gl)
  485. {
  486. super(gl);
  487. this._uniformLocation['image'] = gl.getUniformLocation(this._program, 'image');
  488. this._texture = gl.createTexture();
  489. this._uploaded = false;
  490. this._image = null;
  491. if(this._imageURL != '') {
  492. this._image = new Image();
  493. this._image.onload = () => this.upload(this._image);
  494. this._image.src = this._imageURL;
  495. }
  496. }
  497. upload(data)
  498. {
  499. const gl = this._gl;
  500. gl.bindTexture(gl.TEXTURE_2D, this._texture);
  501. gl.texImage2D(gl.TEXTURE_2D,
  502. 0, // mipmap level
  503. gl.RGBA, // internal format
  504. gl.RGBA, // data format
  505. gl.UNSIGNED_BYTE, // data type
  506. data // data
  507. );
  508. gl.generateMipmap(gl.TEXTURE_2D);
  509. gl.bindTexture(gl.TEXTURE_2D, null);
  510. this._uploaded = true;
  511. }
  512. _render(time)
  513. {
  514. const gl = this._gl;
  515. if(!this._uploaded)
  516. return;
  517. gl.bindTexture(gl.TEXTURE_2D, this._texture);
  518. gl.activeTexture(gl.TEXTURE0 + 0);
  519. gl.uniform1i(this._uniformLocation['image'], 0);
  520. super._render(time);
  521. gl.bindTexture(gl.TEXTURE_2D, null);
  522. }
  523. get _imageURL()
  524. {
  525. return '';
  526. }
  527. get _rectify()
  528. {
  529. return `
  530. uniform sampler2D image;
  531. vec4 rectify(vec4 v)
  532. {
  533. ivec2 size = textureSize(image, 0);
  534. float imageAspect = size.y > 0 ? float(size.x) / float(size.y) : 1.0;
  535. vec4 u = imageAspect > 1.0 ?
  536. vec4(1.0, 1.0 / imageAspect, 1.0, 1.0) :
  537. vec4(imageAspect, 1.0, 1.0, 1.0);
  538. return u * v;
  539. }
  540. `;
  541. }
  542. get fragmentShader()
  543. {
  544. return `#version 300 es
  545. precision mediump float;
  546. uniform sampler2D image;
  547. in vec2 texCoord;
  548. out vec4 color;
  549. ${this._includeFragmentShaderUtils}
  550. void main()
  551. {
  552. vec4 pixel = texture(image, texCoord);
  553. color = colorize(pixel);
  554. }
  555. `;
  556. }
  557. }
  558. class Sprite extends ImageQuad
  559. {
  560. get _numberOfFrames()
  561. {
  562. return 1;
  563. }
  564. get _framesPerSecond()
  565. {
  566. return 8;
  567. }
  568. get _initialFrame()
  569. {
  570. return 0;
  571. }
  572. get _isHorizontalSpritesheet()
  573. {
  574. return true;
  575. }
  576. get _rectify()
  577. {
  578. return `
  579. uniform sampler2D image;
  580. vec4 rectify(vec4 v)
  581. {
  582. const int NUMBER_OF_FRAMES = int(${this._numberOfFrames});
  583. const bool HORIZONTAL = bool(${this._isHorizontalSpritesheet});
  584. ivec2 size = textureSize(image, 0);
  585. size /= HORIZONTAL ? ivec2(NUMBER_OF_FRAMES, 1) : ivec2(1, NUMBER_OF_FRAMES);
  586. float imageAspect = size.y > 0 ? float(size.x) / float(size.y) : 1.0;
  587. vec4 u = imageAspect > 1.0 ?
  588. vec4(1.0, 1.0 / imageAspect, 1.0, 1.0) :
  589. vec4(imageAspect, 1.0, 1.0, 1.0);
  590. return u * v;
  591. }
  592. `;
  593. }
  594. get _rectifyUV()
  595. {
  596. return `
  597. vec2 rectifyUV(vec2 v)
  598. {
  599. const bool HORIZONTAL = bool(${this._isHorizontalSpritesheet});
  600. const float NUMBER_OF_FRAMES = float(${this._numberOfFrames});
  601. const float INITIAL_FRAME = float(${this._initialFrame});
  602. const float FPS = float(${this._framesPerSecond});
  603. float frame = mod(time * FPS + INITIAL_FRAME, NUMBER_OF_FRAMES);
  604. float base = floor(frame) / NUMBER_OF_FRAMES;
  605. vec2 offset = v / NUMBER_OF_FRAMES;
  606. return HORIZONTAL ?
  607. vec2(base + offset.x, v.y) :
  608. vec2(v.x, base + offset.y);
  609. }
  610. `;
  611. }
  612. }
  613. class ItWorks extends ImageQuad
  614. {
  615. constructor(gl)
  616. {
  617. super(gl);
  618. this._image = document.getElementById('it-works');
  619. this.upload(this._image);
  620. }
  621. get _transform()
  622. {
  623. return `
  624. const vec3 POSITION = vec3(0.0, 1.0, 0.0);
  625. const vec3 SCALE = vec3(2.25);
  626. vec4 transform(vec4 v)
  627. {
  628. return translate(POSITION) * scale(SCALE) * v;
  629. }
  630. `;
  631. }
  632. get _colorize()
  633. {
  634. return `
  635. const vec4 COLOR = vec4(173, 255, 47, 255) / 255.0;
  636. vec4 colorize(vec4 p)
  637. {
  638. return p * COLOR;
  639. }
  640. `;
  641. }
  642. }
  643. class WingMan extends Sprite
  644. {
  645. constructor(gl)
  646. {
  647. WingMan._count = WingMan._count || 0;
  648. ++WingMan._count;
  649. super(gl);
  650. this._image = document.getElementById('wing-man');
  651. this.upload(this._image);
  652. }
  653. get _numberOfFrames()
  654. {
  655. return 8;
  656. }
  657. get _framesPerSecond()
  658. {
  659. return 20;
  660. }
  661. get _initialFrame()
  662. {
  663. const n = 2;
  664. return (WingMan._count % n) * Math.floor(this._numberOfFrames / n);
  665. }
  666. get _transform()
  667. {
  668. return `
  669. const float PI = float(${Math.PI});
  670. const float INITIAL_PHASE = PI * float(${WingMan._count});
  671. const vec3 INITIAL_POSITION = vec3(1.25 * cos(INITIAL_PHASE), -0.25, 2.0);
  672. const vec3 SCALE = vec3(0.7);
  673. vec4 transform(vec4 v)
  674. {
  675. float z = 0.6 * cos(INITIAL_PHASE);
  676. float y = 0.1 * cos(2.0 * PI * time + INITIAL_PHASE);
  677. vec3 position = vec3(0.0, y, z) + INITIAL_POSITION;
  678. return translate(position) * scale(SCALE) * v;
  679. }
  680. `;
  681. }
  682. }
  683. /*
  684. MARTINS.js + WebGL code:
  685. */
  686. window.addEventListener('load', async function() {
  687. try {
  688. const session = await startARSession();
  689. const gl = initGL(session.viewport.canvas);
  690. const scene = [
  691. new AnimatedPyramid(gl),
  692. new AnimatedCube(gl),
  693. new ItWorks(gl),
  694. new WingMan(gl),
  695. new WingMan(gl),
  696. ];
  697. function initGL(canvas)
  698. {
  699. const gl = canvas.getContext('webgl2', {
  700. alpha: true
  701. });
  702. if(!gl)
  703. throw new Error(`Can't create WebGL2 context`);
  704. gl.enable(gl.DEPTH_TEST);
  705. gl.enable(gl.BLEND);
  706. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  707. return gl;
  708. }
  709. function clear()
  710. {
  711. gl.clearColor(0, 0, 0, 0);
  712. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  713. }
  714. function render(elapsedTimeInSeconds, projectionMatrix, modelViewMatrix)
  715. {
  716. for(const entity of scene) {
  717. entity.projectionMatrix.set(projectionMatrix);
  718. entity.modelViewMatrix.set(modelViewMatrix);
  719. entity.render(elapsedTimeInSeconds);
  720. }
  721. }
  722. function animate(time, frame)
  723. {
  724. clear();
  725. for(const result of frame.results) {
  726. if('image-tracker' == result.tracker.type) {
  727. if(result.trackables.length > 0) {
  728. const trackable = result.trackables[0];
  729. const projectionMatrix = result.viewer.view.projectionMatrix;
  730. const modelViewMatrix = result.viewer.convertToViewerSpace(trackable.pose).transform.matrix;
  731. render(frame.session.time.elapsed, projectionMatrix.read(), modelViewMatrix.read());
  732. }
  733. }
  734. }
  735. session.requestAnimationFrame(animate);
  736. }
  737. session.requestAnimationFrame(animate);
  738. }
  739. catch(error) {
  740. alert(error.message);
  741. }
  742. async function startARSession()
  743. {
  744. if(!Martins.isSupported()) {
  745. throw new Error(
  746. 'Use a browser/device compatible with WebGL2 and WebAssembly. ' +
  747. 'Your user agent is ' + navigator.userAgent
  748. );
  749. }
  750. //Martins.Settings.powerPreference = 'low-power';
  751. const tracker = Martins.Tracker.ImageTracker();
  752. await tracker.database.add([{
  753. name: 'my-reference-image',
  754. image: document.getElementById('my-reference-image')
  755. }]);
  756. const viewport = Martins.Viewport({
  757. container: document.getElementById('ar-viewport'),
  758. hudContainer: document.getElementById('ar-hud')
  759. });
  760. const video = document.getElementById('my-video');
  761. const useWebcam = (video === null);
  762. const source = useWebcam ?
  763. Martins.Source.Camera({ resolution: 'md' }) :
  764. Martins.Source.Video(video);
  765. const session = await Martins.startSession({
  766. mode: 'immersive',
  767. viewport: viewport,
  768. trackers: [ tracker ],
  769. sources: [ source ],
  770. stats: true,
  771. gizmos: true,
  772. });
  773. const scan = document.getElementById('scan');
  774. tracker.addEventListener('targetfound', event => {
  775. session.gizmos.visible = false;
  776. if(scan)
  777. scan.hidden = true;
  778. });
  779. tracker.addEventListener('targetlost', event => {
  780. session.gizmos.visible = true;
  781. if(scan)
  782. scan.hidden = false;
  783. });
  784. return session;
  785. }
  786. });