Creating a Photo Array in WebGL

Tutorials

Onclick approaching photos array with webgl. Today we continue HTML5 WebGL examples. I prepared a nice photo gallery for you. Images in this gallery will be replaced by a mouse click.

Here are our demo and downloadable package:

Live Demo

[sociallocker]

download in package

[/sociallocker]


Ok, download the example files and lets start coding !


Step 1. HTML

Here are html sources of our demo. As you can see – just empty page.

index.html

01 <!DOCTYPE html>
02 <html lang="en" >
03     <head>
04         <meta charset="utf-8" />
05         <title>Creating photo array in WebGL | Script Tutorials</title>
06         <link href="css/main.css" rel="stylesheet" type="text/css" />
07         <script type="text/javascript" src="js/glMatrix-0.9.5.min.js"></script>
08         <script type="text/javascript" src="js/webgl-utils.js"></script>
09         <script type="text/javascript" src="js/script.js"></script>
10     </head>
11     <body onload="initWebGl();">
12         <div class="container" id="container">
13             <h3>You can use your mouse to switch images</h3>
14             <canvas id="panel" width="800" height="600"></canvas>
15         </div>
16         <footer>
17             <h2>Creating photo array in WebGL</h2>
18             <a href="https://www.script-tutorials.com/creating-photo-array-in-webgl/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
19         </footer>
20     </body>
21 </html>

Step 2. CSS

Here are used CSS styles.

css/main.css

01 *{
02     margin:0;
03     padding:0;
04 }
05 body {
06     /*background-color:#bababa;*/
07     color:#fff;
08     font:14px/1.3 Arial,sans-serif;
09 }
10 footer {
11     background-color:#212121;
12     bottom:0;
13     box-shadow: 0 -1px 2px #111111;
14     display:block;
15     height:70px;
16     left:0;
17     position:fixed;
18     width:100%;
19     z-index:100;
20 }
21 footer h2{
22     font-size:22px;
23     font-weight:normal;
24     left:50%;
25     margin-left:-400px;
26     padding:22px 0;
27     position:absolute;
28     width:540px;
29 }
30 footer a.stuts,a.stuts:visited{
31     border:none;
32     text-decoration:none;
33     color:#fcfcfc;
34     font-size:14px;
35     left:50%;
36     line-height:31px;
37     margin:23px 0 0 110px;
38     position:absolute;
39     top:0;
40 }
41 footer .stuts span {
42     font-size:22px;
43     font-weight:bold;
44     margin-left:5px;
45 }
46 .container {
47     margin:20px auto;
48     position:relative;
49     width:800px;
50 }
51 #panel {
52     cursor:pointer;
53 }

Step 3. JS

js/webgl-utils.js and js/glMatrix-0.9.5.min.js

These files we will use in project for working with WebGL. Both files will in our package.

js/script.js

001 var gl; // global WebGL object
002 var shaderProgram;
003 var pics_names=['1.png''2.png''3.png''4.png''5.png''6.png''7.png'];
004 var pics_num=pics_names.length;
005 // diffirent initializations
006 function initGL(canvas) {
007     try {
008         gl = canvas.getContext('experimental-webgl');
009         gl.viewportWidth = canvas.width;
010         gl.viewportHeight = canvas.height;
011     catch (e) {}
012     if (! gl) {
013         alert('Can`t initialise WebGL, not supported');
014     }
015 }
016 function getShader(gl, type) {
017     var str = '';
018     var shader;
019     if (type == 'x-fragment') {
020         str = "#ifdef GL_ES\n"+
021 "precision highp float;\n"+
022 "#endif\n"+
023 "varying vec2 vTextureCoord;\n"+
024 "uniform sampler2D uSampler;\n"+
025 "void main(void) {\n"+
026 "    gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n"+
027 "}\n";
028         shader = gl.createShader(gl.FRAGMENT_SHADER);
029     else if (type == 'x-vertex') {
030         str = "attribute vec3 aVertexPosition;\n"+
031 "attribute vec2 aTextureCoord;\n"+
032 "uniform mat4 uMVMatrix;\n"+
033 "uniform mat4 uPMatrix;\n"+
034 "varying vec2 vTextureCoord;\n"+
035 "void main(void) {\n"+
036 "    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n"+
037 "    vTextureCoord = aTextureCoord;\n"+
038 "}\n";
039         shader = gl.createShader(gl.VERTEX_SHADER);
040     else {
041         return null;
042     }
043     gl.shaderSource(shader, str);
044     gl.compileShader(shader);
045     if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
046         alert(gl.getShaderInfoLog(shader));
047         return null;
048     }
049     return shader;
050 }
051 function initShaders() {
052     var fragmentShader = getShader(gl, 'x-fragment');
053     var vertexShader = getShader(gl, 'x-vertex');
054     shaderProgram = gl.createProgram();
055     gl.attachShader(shaderProgram, vertexShader);
056     gl.attachShader(shaderProgram, fragmentShader);
057     gl.linkProgram(shaderProgram);
058     if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
059         alert('Can`t initialise shaders');
060     }
061     gl.useProgram(shaderProgram);
062     shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
063     gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
064     shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, 'aTextureCoord');
065     gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
066     shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, 'uPMatrix');
067     shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMVMatrix');
068     shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, 'uSampler');
069 }
070 var objVertexPositionBuffer=new Array();
071 var objVertexTextureCoordBuffer=new Array();
072 var objVertexIndexBuffer=new Array();
073 function initObjBuffers() {
074     for (var i=0;i<pics_num;i=i+1) {
075         objVertexPositionBuffer[i] = gl.createBuffer();
076         gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer[i]);
077         vertices = [
078             -0.5, -0.5, -i * 2,
079             -0.5,  0.5, -i * 2,
080              0.5,  0.5, -i * 2,
081              0.5, -0.5, -i * 2,
082         ];
083         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
084         objVertexPositionBuffer[i].itemSize = 3;
085         objVertexPositionBuffer[i].numItems = 4;
086         objVertexTextureCoordBuffer[i] = gl.createBuffer();
087         gl.bindBuffer(gl.ARRAY_BUFFER,  objVertexTextureCoordBuffer[i] );
088         var textureCoords = [
089             0.0, 0.0,
090             0.0, 1.0,
091             1.0, 1.0,
092             1.0, 0.0,
093         ];
094         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
095         objVertexTextureCoordBuffer[i].itemSize = 2;
096         objVertexTextureCoordBuffer[i].numItems = 4;
097         objVertexIndexBuffer[i] = gl.createBuffer();
098         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, objVertexIndexBuffer[i]);
099         var objVertexIndices = [
100             0, 1, 2,
101             0, 2, 3,
102         ];
103         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(objVertexIndices), gl.STATIC_DRAW);
104         objVertexIndexBuffer[i].itemSize = 1;
105         objVertexIndexBuffer[i].numItems = 6;
106     }
107 }
108 function handleLoadedTexture(texture) {
109     gl.bindTexture(gl.TEXTURE_2D, texture);
110     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
111     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
112     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
113     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
114     gl.bindTexture(gl.TEXTURE_2D, null);
115 }
116 var aTextures = Array();
117 function initTexture(image) {
118     var crateImage = new Image();
119     var texture = gl.createTexture();
120     texture.image = crateImage;
121     crateImage.src = image;
122     crateImage.onload = function () {
123         handleLoadedTexture(texture)
124     }
125     return texture;
126 }
127 function initTextures() {
128     for (var i=0; i < pics_num; i++) {
129         aTextures[i]=initTexture(pics_names[i]);
130     }
131 }
132 var mvMatrix = mat4.create();
133 var mvMatrixStack = [];
134 var pMatrix = mat4.create();
135 function setMatrixUniforms() {
136     gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
137     gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
138 }
139 // variables
140 var z = -2.5;
141 var iImg = 1;
142 var bMove = false;
143 var iTimer = 0;
144 var iStep = 0.03;
145 // mouse handler
146 function handleMouseDown(event) {
147     if (bMove == false && iTimer == 0) {
148         bMove = true;
149         iTimer = 2;
150         iImg++;
151     }
152 }
153 // Draw scene and initialization
154 var MoveMatrix = mat4.create();
155 mat4.identity(MoveMatrix);
156 function drawScene() {
157     gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
158     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
159     mat4.perspective(30, gl.viewportWidth / gl.viewportHeight, 2.0, 100.0, pMatrix);
160     mat4.identity(mvMatrix);
161     mat4.translate(mvMatrix, [0.0, 0.0, z]);
162     mat4.multiply(mvMatrix, MoveMatrix);
163     for (var i=0;i<pics_num;i=i+1) {
164         gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer[i]);
165         gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, objVertexPositionBuffer[i].itemSize, gl.FLOAT, false, 0, 0);
166         gl.bindBuffer(gl.ARRAY_BUFFER, objVertexTextureCoordBuffer[i]);
167         gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, objVertexTextureCoordBuffer[i].itemSize, gl.FLOAT, false, 0, 0);
168         gl.activeTexture(gl.TEXTURE0);
169         gl.bindTexture(gl.TEXTURE_2D, aTextures[i]);
170         gl.uniform1i(shaderProgram.samplerUniform, 0);
171         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, objVertexIndexBuffer[i]);
172         setMatrixUniforms();
173         gl.drawElements(gl.TRIANGLES, objVertexIndexBuffer[i].numItems, gl.UNSIGNED_SHORT, 0);
174     }
175 }
176 var lastTime = 0;
177 function animate() {
178     var timeNow = new Date().getTime();
179     if (lastTime != 0) {
180         if (z > (pics_num - 2) * 2) {
181             z = -4.0;
182             iImg = 1;
183         }
184         if (bMove == true && iTimer > 0) {
185             iTimer -= iStep;
186             z += iStep;
187             if (iTimer <= 0) {
188                 bMove = false;
189                 iTimer = 0;
190             }
191         }
192     }
193     lastTime = timeNow;
194 }
195 function drawFrame() {
196     requestAnimFrame(drawFrame);
197     drawScene();
198     animate();
199 }
200 function initWebGl() {
201     var canvas = document.getElementById('panel');
202     initGL(canvas);
203     initShaders();
204     initObjBuffers();
205     initTextures();
206     gl.clearColor(1.0, 1.0, 1.0, 1.0);
207     gl.enable(gl.DEPTH_TEST);
208     canvas.onmousedown = handleMouseDown;
209     drawFrame();
210 }

This is pretty long code, but it is most important. I separated all code to 3 sides: initializations, handlers and drawing of scene. Hope that you already read our previos post where I told about creating another photo gallery with WebGL. In this case it will more easy to understand today’s code. Today we handle click events for other purposes. We’ll run some internal timer – and will change the Z coordinate on the timer (in the end – the pictures are moving forward, and then disappear).

Step 4. Images

All these images we will using for twisting:

    1
    2
    3
    4
    5
    6
    7

Live Demo

Conclusion

I hope you enjoyed today`s result. If you have any suggestions or ideas – share it 🙂 Welcome back our friends!

Rate article