Today we continue HTML5 canvas examples. I use WebGL technology in order to map a sequence of images on a rotating cube. Cube is not just spinning all the time, but also makes a little pause between images.
Here are our demo and downloadable package:
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
04 | < meta charset = "utf-8" /> |
05 | < title >WebGL Box photo slideshow | Script Tutorials</ title > |
07 | < link href = "css/main.css" rel = "stylesheet" type = "text/css" /> |
08 | < script src = "js/glMatrix-0.9.5.min.js" ></ script > |
09 | < script src = "js/webgl-utils.js" ></ script > |
10 | < script src = "js/script.js" ></ script > |
12 | < body onload = "initWebGl()" > |
13 | < div class = "container" > |
14 | < canvas id = "panel" width = "800" height = "600" ></ canvas > |
17 | < h2 >WebGL Box photo slideshow</ h2 > |
Step 2. CSS
Here are used CSS styles.
css/main.css
06 | background-repeat : no-repeat ; |
07 | background-color : #bababa ; |
08 | background-image : -webkit-radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
09 | background-image : -moz-radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
10 | background-image : -o-radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
11 | background-image : radial-gradient( 600px 200px , circle , #eee , #bababa 40% ); |
13 | font : 14px / 1.3 Arial , sans-serif ; |
17 | background-color : #212121 ; |
19 | box-shadow: 0 -1px 2px #111111 ; |
36 | footer a.stuts,a.stuts:visited{ |
43 | margin : 23px 0 0 110px ; |
53 | border : 3px #111 solid ; |
60 | -moz-border-radius: 15px ; |
61 | -webkit-border-radius: 15px ; |
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
016 | var pics_num=pics_names.length; |
020 | function initGL(canvas) { |
022 | gl = canvas.getContext( 'experimental-webgl' ); |
023 | gl.viewportWidth = canvas.width; |
024 | gl.viewportHeight = canvas.height; |
027 | alert( 'Can`t initialise WebGL, not supported' ); |
031 | function getShader(gl, type) { |
035 | if (type == 'x-fragment' ) { |
036 | str = "#ifdef GL_ES\n" + |
037 | "precision highp float;\n" + |
039 | "varying vec2 vTextureCoord;\n" + |
040 | "uniform sampler2D uSampler;\n" + |
041 | "void main(void) {\n" + |
042 | " gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n" + |
044 | shader = gl.createShader(gl.FRAGMENT_SHADER); |
045 | } else if (type == 'x-vertex' ) { |
046 | str = "attribute vec3 aVertexPosition;\n" + |
047 | "attribute vec2 aTextureCoord;\n" + |
048 | "uniform mat4 uMVMatrix;\n" + |
049 | "uniform mat4 uPMatrix;\n" + |
050 | "varying vec2 vTextureCoord;\n" + |
051 | "void main(void) {\n" + |
052 | " gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n" + |
053 | " vTextureCoord = aTextureCoord;\n" + |
055 | shader = gl.createShader(gl.VERTEX_SHADER); |
060 | gl.shaderSource(shader, str); |
061 | gl.compileShader(shader); |
063 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { |
064 | alert(gl.getShaderInfoLog(shader)); |
070 | function initShaders() { |
071 | var fragmentShader = getShader(gl, 'x-fragment' ); |
072 | var vertexShader = getShader(gl, 'x-vertex' ); |
074 | shaderProgram = gl.createProgram(); |
075 | gl.attachShader(shaderProgram, vertexShader); |
076 | gl.attachShader(shaderProgram, fragmentShader); |
077 | gl.linkProgram(shaderProgram); |
079 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { |
080 | alert( 'Can`t initialise shaders' ); |
083 | gl.useProgram(shaderProgram); |
085 | shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aVertexPosition' ); |
086 | gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); |
088 | shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, 'aTextureCoord' ); |
089 | gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); |
091 | shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, 'uPMatrix' ); |
092 | shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMVMatrix' ); |
093 | shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, 'uSampler' ); |
096 | var objVertexPositionBuffer= new Array(); |
097 | var objVertexTextureCoordBuffer= new Array(); |
098 | var objVertexIndexBuffer= new Array(); |
100 | function initObjBuffers() { |
101 | for ( var i=0;i<4;i=i+1) { |
102 | objVertexPositionBuffer[i] = gl.createBuffer(); |
103 | gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer[i]); |
105 | Math.cos(i*((2*Math.PI)/4)), -0.5, Math.sin(i*((2*Math.PI)/4)), |
106 | Math.cos(i*((2*Math.PI)/4)), 0.5, Math.sin(i*((2*Math.PI)/4)), |
107 | Math.cos((i+1)*((2*Math.PI)/4)), 0.5, Math.sin((i+1)*((2*Math.PI)/4)), |
108 | Math.cos((i+1)*((2*Math.PI)/4)), -0.5, Math.sin((i+1)*((2*Math.PI)/4)), |
110 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); |
111 | objVertexPositionBuffer[i].itemSize = 3; |
112 | objVertexPositionBuffer[i].numItems = 4; |
114 | objVertexTextureCoordBuffer[i] = gl.createBuffer(); |
115 | gl.bindBuffer(gl.ARRAY_BUFFER, objVertexTextureCoordBuffer[i] ); |
116 | var textureCoords = [ |
122 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW); |
123 | objVertexTextureCoordBuffer[i].itemSize = 2; |
124 | objVertexTextureCoordBuffer[i].numItems = 4; |
126 | objVertexIndexBuffer[i] = gl.createBuffer(); |
127 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, objVertexIndexBuffer[i]); |
128 | var objVertexIndices = [ |
132 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(objVertexIndices), gl.STATIC_DRAW); |
133 | objVertexIndexBuffer[i].itemSize = 1; |
134 | objVertexIndexBuffer[i].numItems = 6; |
138 | function handleLoadedTexture(texture) { |
139 | gl.bindTexture(gl.TEXTURE_2D, texture); |
140 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true ); |
141 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image); |
142 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); |
143 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); |
144 | gl.bindTexture(gl.TEXTURE_2D, null ); |
147 | var crateTextures = Array(); |
148 | var usedTextures = Array(); |
149 | function initTexture(image) { |
150 | var texture = gl.createTexture(); |
151 | texture.image = new Image(); |
153 | texture.image.onload = function () { |
154 | handleLoadedTexture(texture); |
156 | texture.image.src = image; |
160 | function initTextures() { |
161 | for ( var i=0; i < pics_num; i++) { |
162 | crateTextures[i]=initTexture(pics_names[i]); |
164 | usedTextures = crateTextures.slice(0, 2); |
167 | var mvMatrix = mat4.create(); |
168 | var mvMatrixStack = []; |
169 | var pMatrix = mat4.create(); |
171 | function setMatrixUniforms() { |
172 | gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false , pMatrix); |
173 | gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false , mvMatrix); |
176 | function degToRad(degrees) { |
177 | return degrees * Math.PI / 180; |
186 | var RotationMatrix = mat4.create(); |
187 | mat4.identity(RotationMatrix); |
191 | var MoveMatrix = mat4.create(); |
192 | mat4.identity(MoveMatrix); |
198 | function drawScene() { |
199 | gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); |
200 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
202 | mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix); |
203 | mat4.identity(mvMatrix); |
204 | mat4.translate(mvMatrix, [0.0, 0.0, z]); |
205 | mat4.rotate(mvMatrix, degToRad(yRot), [0, 1, 0]); |
206 | mat4.multiply(mvMatrix, MoveMatrix); |
207 | mat4.multiply(mvMatrix, RotationMatrix); |
209 | iStep = (parseInt((yRot+45) / 90)) % pics_num; |
211 | if (iCurStep != iStep) { |
214 | var bChange = (iCurStep) % 2; |
215 | var bChange2 = (iCurStep+1) % 2; |
217 | if (iCurStep+2 > pics_num) { |
219 | usedTextures[0] = crateTextures[0]; |
224 | usedTextures[0] = crateTextures[iCurStep+1]; |
227 | usedTextures[1] = crateTextures[iCurStep+1]; |
231 | for ( var i=0;i<4;i=i+1) { |
232 | gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer[i]); |
233 | gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, objVertexPositionBuffer[i].itemSize, gl.FLOAT, false , 0, 0); |
235 | gl.bindBuffer(gl.ARRAY_BUFFER, objVertexTextureCoordBuffer[i]); |
236 | gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, objVertexTextureCoordBuffer[i].itemSize, gl.FLOAT, false , 0, 0); |
238 | gl.activeTexture(gl.TEXTURE0); |
239 | gl.bindTexture(gl.TEXTURE_2D, usedTextures[i % 2]); |
240 | gl.uniform1i(shaderProgram.samplerUniform, 0); |
242 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, objVertexIndexBuffer[i]); |
244 | gl.drawElements(gl.TRIANGLES, objVertexIndexBuffer[i].numItems, gl.UNSIGNED_SHORT, 0); |
251 | var timeNow = new Date().getTime(); |
252 | if (lastTime != 0 && bPause == false ) { |
253 | var elapsed = timeNow - lastTime; |
254 | yRot += (ySpeed * elapsed) / 1000.0; |
256 | if (bPause == true ) { |
257 | var elapsed = timeNow - lastTime; |
259 | if (iElapsed > iPause) { |
267 | function drawFrame() { |
268 | requestAnimFrame(drawFrame); |
273 | function initWebGl() { |
274 | var canvas = document.getElementById( 'panel' ); |
280 | gl.clearColor(0.0, 0.0, 0.0, 1.0); |
281 | gl.enable(gl.DEPTH_TEST); |
Hope that you already read similar tutorial – Creating a Photo Array in WebGL. In this case it will more easy to understand today`s code. I have made several changes in this code: I have removed all mouse/keyboard handlers, and have done most of changes in drawScene function.
Conclusion
I hope you enjoyed today`s result. If you have any suggestions or ideas – share it
Welcome back our friends!