Просмотр исходного кода

Add online demo based on WebGL

customisations
alemart 3 лет назад
Родитель
Сommit
6bce869244
14 измененных файлов: 1065 добавлений и 6 удалений
  1. 22
    1
      README.md
  2. 4
    0
      docs/demo/index.md
  3. 32
    0
      docs/demo/instructions.md
  4. Двоичные данные
      docs/demo/my-reference-image.webp
  5. Двоичные данные
      docs/demo/qr-code.png
  6. 1
    3
      docs/getting-started/index.md
  7. Двоичные данные
      docs_overrides/demo/bird.png
  8. 10
    0
      docs_overrides/demo/demo.css
  9. 968
    0
      docs_overrides/demo/demo.js
  10. 21
    0
      docs_overrides/demo/index.html
  11. Двоичные данные
      docs_overrides/demo/it-works.png
  12. Двоичные данные
      docs_overrides/demo/scan.png
  13. 4
    1
      docs_overrides/home.html
  14. 3
    1
      mkdocs.yml

+ 22
- 1
README.md Просмотреть файл

@@ -4,7 +4,7 @@ Create amazing Augmented Reality experiences with **MARTINS.js**, a GPU-accelera
4 4
 
5 5
 Get started at <https://alemart.github.io/martins-js/>
6 6
 
7
-![WebAR demo](docs/img/demo-cool3.gif) ![WebAR demo](docs/img/splash.gif)
7
+![WebAR demo](docs/img/demo-cool3.gif)
8 8
 
9 9
 MARTINS.js is [dual licensed](#author). It is currently in beta.
10 10
 
@@ -16,6 +16,27 @@ Currently supported features:
16 16
 
17 17
 ![WebAR demo](docs/img/demo-aframe.gif) ![WebAR demo](docs/img/demo-hello.gif)
18 18
 
19
+## Try WebAR right now!
20
+
21
+1. Scan or tap the QR code below with a mobile device.
22
+2. A web page will be opened. It's the WebAR experience.
23
+3. The web page will request access to your webcam. Authorize it.
24
+4. Scan the cartoon below.
25
+5. Enjoy! :wink:
26
+
27
+> &nbsp;
28
+> **Guidelines for WebAR:**
29
+> - WebGL2 and WebAssembly are required. Use a [compatible browser](#browser-compatibility).
30
+> - Don't move the camera too quickly - it produces motion blur.
31
+> - The physical environment should be properly illuminated.
32
+> - Avoid low-quality cameras (cameras of common smartphones are OK).
33
+> &nbsp;
34
+
35
+| Cartoon | QR code |
36
+| - | - |
37
+| [![Reference Image](docs/demo/my-reference-image.webp)](docs/demo/my-reference-image.webp) | [![QR code](docs/demo/qr-code.png)](https://alemart.github.io/martins-js/demo/) |
38
+
39
+
19 40
 ## Why use MARTINS.js?
20 41
 
21 42
 Here is why MARTINS.js is a great choice for creating Augmented Reality experiences:

+ 4
- 0
docs/demo/index.md Просмотреть файл

@@ -0,0 +1,4 @@
1
+---
2
+template: demo/index.html
3
+title: ""
4
+---

+ 32
- 0
docs/demo/instructions.md Просмотреть файл

@@ -0,0 +1,32 @@
1
+---
2
+title: Try WebAR right now!
3
+---
4
+
5
+# Try WebAR right now!
6
+
7
+## Follow the steps
8
+
9
+1. Scan or tap the QR code below with a mobile device.
10
+2. A web page will be opened. It's the WebAR experience.
11
+3. The web page will request access to your webcam. Authorize it.
12
+4. Scan the cartoon below.
13
+5. Enjoy! :wink:
14
+
15
+!!! info "Guidelines for WebAR"
16
+
17
+    - WebGL2 and WebAssembly are required. Use a [compatible browser](../getting-started/questions-and-answers.md#what-about-browser-compatibility).
18
+    - Don't move the camera too quickly - it produces motion blur.
19
+    - The physical environment should be properly illuminated.
20
+    - Avoid low-quality cameras (cameras of common smartphones are OK).
21
+
22
+## Scan the QR code
23
+
24
+[![QR code](qr-code.png){ width=320 }](index.md){ ._blank }
25
+
26
+## Scan the cartoon
27
+
28
+[![Reference Image](my-reference-image.webp){ width=600 }](my-reference-image.webp){ ._blank }
29
+
30
+## Wanna see more?
31
+
32
+[Check out my WebAR demos](../demos.md){ .md-button }

Двоичные данные
docs/demo/my-reference-image.webp Просмотреть файл


Двоичные данные
docs/demo/qr-code.png Просмотреть файл


+ 1
- 3
docs/getting-started/index.md Просмотреть файл

@@ -4,7 +4,7 @@
4 4
 
5 5
 **MARTINS.js** is a GPU-accelerated Augmented Reality engine for the web. It's a standalone WebAR technology for creating AR experiences that run in web browsers. Users don't need specialized hardware nor dedicated software - only a modern and compatible web browser.
6 6
 
7
-[Get started with WebAR](./introduction.md){ .md-button .md-button--primary }
7
+[Learn WebAR](./introduction.md){ .md-button .md-button--primary } [Try a demo](../demo/instructions.md){ .md-button }
8 8
 
9 9
 ## Features
10 10
 
@@ -24,8 +24,6 @@ Here is why MARTINS.js is a great choice for creating Augmented Reality experien
24 24
 * **Fully standalone!** MARTINS.js has in it everything it needs to analyze the environment and help you create AR. There are no additional requirements. It's not WebXR.
25 25
 * **Easy to get started!** MARTINS.js can be used with a `<script>` tag in your page. A static HTML page is enough to get started.
26 26
 
27
-[Check out my WebAR demos](../demos.md){ .md-button }
28
-
29 27
 ## Browser compatibility
30 28
 
31 29
 MARTINS.js is currently compatible with the latest versions of almost all major web browsers:

Двоичные данные
docs_overrides/demo/bird.png Просмотреть файл


+ 10
- 0
docs_overrides/demo/demo.css Просмотреть файл

@@ -0,0 +1,10 @@
1
+body {
2
+    background-color: #3d5afe;
3
+}
4
+
5
+#scan {
6
+    width: 100%;
7
+    height: 100%;
8
+    object-fit: contain;
9
+    opacity: 0.75;
10
+}

+ 968
- 0
docs_overrides/demo/demo.js Просмотреть файл

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

+ 21
- 0
docs_overrides/demo/index.html Просмотреть файл

@@ -0,0 +1,21 @@
1
+<!doctype html>
2
+<html>
3
+    <head>
4
+        <meta charset="utf-8">
5
+        <meta name="viewport" content="width=device-width,initial-scale=1">
6
+        <title>MARTINS.js WebAR demo</title>
7
+        <link href="{{ 'demo/demo.css' | url }}" rel="stylesheet">
8
+        <script src="https://cdn.jsdelivr.net/gh/alemart/martins-js@0.1/dist/martins.min.js"></script>
9
+        <script src="{{ 'demo/demo.js' | url }}"></script>
10
+    </head>
11
+    <body>
12
+        <div id="ar-viewport">
13
+            <div id="ar-hud" hidden>
14
+                <img id="scan" src="{{ 'demo/scan.png' | url }}">
15
+            </div>
16
+        </div>
17
+        <img id="my-reference-image" src="{{ 'demo/my-reference-image.webp' | url }}" hidden>
18
+        <img id="it-works" src="{{ 'demo/it-works.png' | url }}" hidden>
19
+        <img id="bird" src="{{ 'demo/bird.png' | url }}" hidden>
20
+    </body>
21
+</html>

Двоичные данные
docs_overrides/demo/it-works.png Просмотреть файл


Двоичные данные
docs_overrides/demo/scan.png Просмотреть файл


+ 4
- 1
docs_overrides/home.html Просмотреть файл

@@ -25,6 +25,8 @@
25 25
     --md-primary-fg-color: var(--md-secondary-fg-color);
26 26
     --md-accent-fg-color: var(--md-secondary-fg-color);
27 27
     --md-accent-bg-color: var(--md-primary-fg-color--light);
28
+    margin-top: 8px;
29
+    margin-bottom: 8px;
28 30
 }
29 31
 
30 32
 #splash {
@@ -109,7 +111,8 @@
109 111
         <div class="splash-text md-typeset appear">
110 112
             <h2><em>Create Augmented Reality experiences that run in web browsers.</em> No need to download apps.</h2>
111 113
             <p>Make AR easy for your users. Create AR experiences using web technologies that you're already familiar with. Open Source WebAR engine.</p>
112
-            <a href="{{ './getting-started' | url }}" class="md-button">Get started</a>
114
+            <a href="{{ './getting-started' | url }}" class="md-button md-button--primary">Get started</a>
115
+            <a href="{{ './demo/instructions' | url }}" class="md-button">Try a demo</a>
113 116
         </div>
114 117
         <div class="splash-image">
115 118
             <video src="{{ './img/splash.webm' | url }}" poster="{{ './img/splash.webp' | url }}" muted loop playsinline autoplay oncanplay="this.muted=true;this.play()"></video>

+ 3
- 1
mkdocs.yml Просмотреть файл

@@ -69,7 +69,9 @@ nav:
69 69
     - 'Licenses':
70 70
       - 'AGPL 3.0': 'license/agpl-3.0.md'
71 71
       - 'Polyform Perimeter 1.0.0': 'license/PolyForm-Perimeter-1.0.0-1.md'
72
-  - 'Demos': 'demos.md'
72
+  - 'Demos':
73
+    - 'Demo gallery': 'demos.md'
74
+    - 'Try it online!': 'demo/instructions.md'
73 75
   - 'API':
74 76
     - 'General':
75 77
       - 'Session': 'api/session.md'

Загрузка…
Отмена
Сохранить