選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

demo.js 22KB

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