DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

How does AI transform chaos engineering from an experiment into a critical capability? Learn how to effectively operationalize the chaos.

Data quality isn't just a technical issue: It impacts an organization's compliance, operational efficiency, and customer satisfaction.

Are you a front-end or full-stack developer frustrated by front-end distractions? Learn to move forward with tooling and clear boundaries.

Developer Experience: Demand to support engineering teams has risen, and there is a shift from traditional DevOps to workflow improvements.

Trending

  • Converting List to String in Terraform
  • Taming Billions of Rows: How Metadata and SQL Can Replace Your ETL Pipeline
  • From ETL to ELT to Real-Time: Modern Data Engineering with Databricks Lakehouse
  • AI Agents in PHP with Model Context Protocol

Spinning Images in WebGL

By 
Andrey Prikaznov user avatar
Andrey Prikaznov
·
Jan. 15, 12 · Interview
Likes (0)
Comment
Save
Tweet
Share
8.3K Views

Join the DZone community and get the full member experience.

Join For Free
today we continue html5 canvas examples. and today is our second tutorial for webgl. we will be creating animated twisting images. also we will add handlers to manipulate the images with mouse and keyboard.

here are our demo and downloadable package:

live demo

download in 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 an empty page.

index.html

<!doctype html>
<html lang="en" >
<head>
    <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>
    <title>creating a keyboard sensitive 3d twisted images in webgl | script tutorials</title>
</head>
<body onload="initwebgl();">
    <div class="example">
        <h3><a href="http://www.script-tutorials.com/twisting-images-webgl/">creating a keyboard sensitive 3d twisted images in webgl | script tutorials</a></h3>
        <h3>you can use your mouse + arrow keys + page up / down</h3>
        <canvas id="panel" width="500" height="333"></canvas>
        <div style="clear:both;"></div>
    </div>
</body>
</html>

step 2. css

here are used css styles.

css/main.css

body {
    background:#eee;
    font-family:verdana, helvetica, arial, sans-serif;
    margin:0;
    padding:0
}
.example {
    background:#fff;
    width:500px;
    font-size:80%;
    border:1px #000 solid;
    margin:20px auto;
    padding:15px;
    position:relative;
    -moz-border-radius: 3px;
    -webkit-border-radius:3px
}
h3 {
    text-align:center;
}

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 = [
            math.cos(i*((2*math.pi)/pics_num)), -0.5,  math.sin(i*((2*math.pi)/pics_num)),
            math.cos(i*((2*math.pi)/pics_num)), 0.5,  math.sin(i*((2*math.pi)/pics_num)),
            math.cos((i+1)*((2*math.pi)/pics_num)), 0.5, math.sin((i+1)*((2*math.pi)/pics_num)),
            math.cos((i+1)*((2*math.pi)/pics_num)), -0.5,  math.sin((i+1)*((2*math.pi)/pics_num)),
        ];
        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 cratetextures = 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++) {
        cratetextures[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);
}

function degtorad(degrees) {
    return degrees * math.pi / 180;
}

// mouse and keyboard handlers

var xrot = 0;
var xspeed = 0;
var yrot = 0;
var yspeed = 10;
var z = -3.0;

var currentlypressedkeys = {};
function handlekeydown(event) {
    currentlypressedkeys[event.keycode] = true;
}

function handlekeyup(event) {
    currentlypressedkeys[event.keycode] = false;
}

function handlekeys() {
    if (currentlypressedkeys[33]) { // page up
        z -= 0.05;
    }
    if (currentlypressedkeys[34]) { // page down
        z += 0.05;
    }
    if (currentlypressedkeys[37]) { // left cursor key
        yspeed -= 1;
    }
    if (currentlypressedkeys[39]) { // right cursor key
        yspeed += 1;
    }
    if (currentlypressedkeys[38]) { // up cursor key
        xspeed -= 1;
    }
    if (currentlypressedkeys[40]) { // down cursor key
        xspeed += 1;
    }
}

var mousedown = false;
var lastmousex = null;
var lastmousey = null;

var rotationmatrix = mat4.create();
mat4.identity(rotationmatrix);

function handlemousedown(event) {
    mousedown = true;
    lastmousex = event.clientx;
    lastmousey = event.clienty;
}

function handlemouseup(event) {
    mousedown = false;
}

function handlemousemove(event) {
    if (!mousedown) {
      return;
    }
    var newx = event.clientx;
    var newy = event.clienty;

    var deltax = newx - lastmousex;
    var newrotationmatrix = mat4.create();
    mat4.identity(newrotationmatrix);
    mat4.rotate(newrotationmatrix, degtorad(deltax / 5), [0, 1, 0]);

    var deltay = newy - lastmousey;
    mat4.rotate(newrotationmatrix, degtorad(deltay / 5), [1, 0, 0]);

    mat4.multiply(newrotationmatrix, rotationmatrix, rotationmatrix);

    lastmousex = newx
    lastmousey = newy;
}

// 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(45, gl.viewportwidth / gl.viewportheight, 0.1, 100.0, pmatrix);

    mat4.identity(mvmatrix);

    mat4.translate(mvmatrix, [0.0, 0.0, z]);

    mat4.rotate(mvmatrix, degtorad(xrot), [1, 0, 0]);
    mat4.rotate(mvmatrix, degtorad(yrot), [0, 1, 0]);

    mat4.multiply(mvmatrix, movematrix);
    mat4.multiply(mvmatrix, rotationmatrix);

    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, cratetextures[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) {
        var elapsed = timenow - lasttime;

        xrot += (xspeed * elapsed) / 1000.0;
        yrot += (yspeed * elapsed) / 1000.0;
    }
    lasttime = timenow;
}

function drawframe() {
    requestanimframe(drawframe);
    handlekeys();
    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);

    document.onkeydown = handlekeydown;
    document.onkeyup = handlekeyup;

    canvas.onmousedown = handlemousedown;
    document.onmouseup = handlemouseup;
    document.onmousemove = handlemousemove;

    drawframe();
}

and again – long code, but most important. i separated all code to 3 sides: initializations, handlers and drawing of scene. hope that you already read our previos webgl lesson . in this case it will more easy to understand today`s code. just make attention that instead color buffer we will using texture buffer (objvertextexturecoordbuffer). also, this demo able to work with any amouht of used images (better – more than 3).

step 4. images

all these images we will using for twisting:

    1
    2
    3
    4
    5
    6
    7


live demo

download in package


conclusion

i hope you enjoyed today's result. if you have any suggestions or ideas – share them :-) welcome back, friends!
WebGL

Opinions expressed by DZone contributors are their own.

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: