Creating a Photo Array in WebGL

Creating a Photo Array in WebGL

0 27530
Creating photo array in WebGL

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

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <title>Creating photo array in WebGL | Script Tutorials</title>
        <link href="css/main.css" rel="stylesheet" type="text/css" />
        <script type="text/javascript" src="js/glMatrix-0.9.5.min.js"></script>
        <script type="text/javascript" src="js/webgl-utils.js"></script>
        <script type="text/javascript" src="js/script.js"></script>
    </head>
    <body onload="initWebGl();">
        <div class="container" id="container">
            <h3>You can use your mouse to switch images</h3>
            <canvas id="panel" width="800" height="600"></canvas>
        </div>
        <footer>
            <h2>Creating photo array in WebGL</h2>
            <a href="https://www.script-tutorials.com/creating-photo-array-in-webgl/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
        </footer>
    </body>
</html>

Step 2. CSS

Here are used CSS styles.

css/main.css

*{
    margin:0;
    padding:0;
}
body {
    /*background-color:#bababa;*/
    color:#fff;
    font:14px/1.3 Arial,sans-serif;
}
footer {
    background-color:#212121;
    bottom:0;
    box-shadow: 0 -1px 2px #111111;
    display:block;
    height:70px;
    left:0;
    position:fixed;
    width:100%;
    z-index:100;
}
footer h2{
    font-size:22px;
    font-weight:normal;
    left:50%;
    margin-left:-400px;
    padding:22px 0;
    position:absolute;
    width:540px;
}
footer a.stuts,a.stuts:visited{
    border:none;
    text-decoration:none;
    color:#fcfcfc;
    font-size:14px;
    left:50%;
    line-height:31px;
    margin:23px 0 0 110px;
    position:absolute;
    top:0;
}
footer .stuts span {
    font-size:22px;
    font-weight:bold;
    margin-left:5px;
}
.container {
    margin:20px auto;
    position:relative;
    width:800px;
}
#panel {
    cursor:pointer;
}

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

var gl; // global WebGL object
var shaderProgram;
var pics_names=['1.png', '2.png', '3.png', '4.png', '5.png', '6.png', '7.png'];
var pics_num=pics_names.length;
// diffirent initializations
function initGL(canvas) {
    try {
        gl = canvas.getContext('experimental-webgl');
        gl.viewportWidth = canvas.width;
        gl.viewportHeight = canvas.height;
    } catch (e) {}
    if (! gl) {
        alert('Can`t initialise WebGL, not supported');
    }
}
function getShader(gl, type) {
    var str = '';
    var shader;
    if (type == 'x-fragment') {
        str = "#ifdef GL_ES\n"+
"precision highp float;\n"+
"#endif\n"+
"varying vec2 vTextureCoord;\n"+
"uniform sampler2D uSampler;\n"+
"void main(void) {\n"+
"    gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n"+
"}\n";
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (type == 'x-vertex') {
        str = "attribute vec3 aVertexPosition;\n"+
"attribute vec2 aTextureCoord;\n"+
"uniform mat4 uMVMatrix;\n"+
"uniform mat4 uPMatrix;\n"+
"varying vec2 vTextureCoord;\n"+
"void main(void) {\n"+
"    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n"+
"    vTextureCoord = aTextureCoord;\n"+
"}\n";
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }
    gl.shaderSource(shader, str);
    gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }
    return shader;
}
function initShaders() {
    var fragmentShader = getShader(gl, 'x-fragment');
    var vertexShader = getShader(gl, 'x-vertex');
    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert('Can`t initialise shaders');
    }
    gl.useProgram(shaderProgram);
    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
    shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, 'aTextureCoord');
    gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, 'uPMatrix');
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMVMatrix');
    shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, 'uSampler');
}
var objVertexPositionBuffer=new Array();
var objVertexTextureCoordBuffer=new Array();
var objVertexIndexBuffer=new Array();
function initObjBuffers() {
    for (var i=0;i<pics_num;i=i+1) {
        objVertexPositionBuffer[i] = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer[i]);
        vertices = [
            -0.5, -0.5, -i * 2,
            -0.5,  0.5, -i * 2,
             0.5,  0.5, -i * 2,
             0.5, -0.5, -i * 2,
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        objVertexPositionBuffer[i].itemSize = 3;
        objVertexPositionBuffer[i].numItems = 4;
        objVertexTextureCoordBuffer[i] = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER,  objVertexTextureCoordBuffer[i] );
        var textureCoords = [
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
        objVertexTextureCoordBuffer[i].itemSize = 2;
        objVertexTextureCoordBuffer[i].numItems = 4;
        objVertexIndexBuffer[i] = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, objVertexIndexBuffer[i]);
        var objVertexIndices = [
            0, 1, 2,
            0, 2, 3,
        ];
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(objVertexIndices), gl.STATIC_DRAW);
        objVertexIndexBuffer[i].itemSize = 1;
        objVertexIndexBuffer[i].numItems = 6;
    }
}
function handleLoadedTexture(texture) {
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.bindTexture(gl.TEXTURE_2D, null);
}
var aTextures = Array();
function initTexture(image) {
    var crateImage = new Image();
    var texture = gl.createTexture();
    texture.image = crateImage;
    crateImage.src = image;
    crateImage.onload = function () {
        handleLoadedTexture(texture)
    }
    return texture;
}
function initTextures() {
    for (var i=0; i < pics_num; i++) {
        aTextures[i]=initTexture(pics_names[i]);
    }
}
var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();
function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
// variables
var z = -2.5;
var iImg = 1;
var bMove = false;
var iTimer = 0;
var iStep = 0.03;
// mouse handler
function handleMouseDown(event) {
    if (bMove == false && iTimer == 0) {
        bMove = true;
        iTimer = 2;
        iImg++;
    }
}
// Draw scene and initialization
var MoveMatrix = mat4.create();
mat4.identity(MoveMatrix);
function drawScene() {
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    mat4.perspective(30, gl.viewportWidth / gl.viewportHeight, 2.0, 100.0, pMatrix);
    mat4.identity(mvMatrix);
    mat4.translate(mvMatrix, [0.0, 0.0, z]);
    mat4.multiply(mvMatrix, MoveMatrix);
    for (var i=0;i<pics_num;i=i+1) {
        gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer[i]);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, objVertexPositionBuffer[i].itemSize, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, objVertexTextureCoordBuffer[i]);
        gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, objVertexTextureCoordBuffer[i].itemSize, gl.FLOAT, false, 0, 0);
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, aTextures[i]);
        gl.uniform1i(shaderProgram.samplerUniform, 0);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, objVertexIndexBuffer[i]);
        setMatrixUniforms();
        gl.drawElements(gl.TRIANGLES, objVertexIndexBuffer[i].numItems, gl.UNSIGNED_SHORT, 0);
    }
}
var lastTime = 0;
function animate() {
    var timeNow = new Date().getTime();
    if (lastTime != 0) {
        if (z > (pics_num - 2) * 2) {
            z = -4.0;
            iImg = 1;
        }
        if (bMove == true && iTimer > 0) {
            iTimer -= iStep;
            z += iStep;
            if (iTimer <= 0) {
                bMove = false;
                iTimer = 0;
            }
        }
    }
    lastTime = timeNow;
}
function drawFrame() {
    requestAnimFrame(drawFrame);
    drawScene();
    animate();
}
function initWebGl() {
    var canvas = document.getElementById('panel');
    initGL(canvas);
    initShaders();
    initObjBuffers();
    initTextures();
    gl.clearColor(1.0, 1.0, 1.0, 1.0);
    gl.enable(gl.DEPTH_TEST);
    canvas.onmousedown = handleMouseDown;
    drawFrame();
}

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!

SIMILAR ARTICLES

Understanding Closures

0 24610

NO COMMENTS

Leave a Reply