Creating an Animated 3D WebGL Demonstration

Creating an Animated 3D WebGL Demonstration

1 44930
Making 3D WebGL demonstration

3D WebGL demonstration tutorial. Today we continue HTML5 canvas examples. And today we will try to begin learning WebGL. In my demonstration I will show you how to initialize WebGL and draw simple 3D object. Also we will animate this object too.

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 source code of our demo. As you can see – just 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>WebGL demonstration | Script Tutorials</title>
</head>
<body onload="initWebGl();">
    <div class="example">
        <h3><a href="https://www.script-tutorials.com/making-3d-webgl-demonstration/">WebGL demonstration | Script Tutorials</a></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;
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 vec4 vColor;\n"+
"void main(void) {\n"+
"    gl_FragColor = vColor;\n"+
"}\n";
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (type == 'x-vertex') {
        str = "attribute vec3 aVertexPosition;\n"+
"attribute vec4 aVertexColor;\n"+
"uniform mat4 uMVMatrix;\n"+
"uniform mat4 uPMatrix;\n"+
"varying vec4 vColor;\n"+
"void main(void) {\n"+
"    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n"+
"    vColor = aVertexColor;\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.vertexColorAttribute = gl.getAttribLocation(shaderProgram, 'aVertexColor');
    gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, 'uPMatrix');
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMVMatrix');
}
var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();
function mvPushMatrix() {
    var copy = mat4.create();
    mat4.set(mvMatrix, copy);
    mvMatrixStack.push(copy);
}
function mvPopMatrix() {
    if (mvMatrixStack.length == 0) {
        throw 'Invalid popMatrix!';
    }
    mvMatrix = mvMatrixStack.pop();
}
function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
function degToRad(degrees) {
    return degrees * Math.PI / 180;
}
var objVertexPositionBuffer;
var objVertexColorBuffer;
function initObjBuffers() {
    objVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer);
    var vertices = [
         1.0,  2.0,  -1.0,
        -1.0,  2.0,  -1.0,
         1.0, -2.0,  -1.0,
        -1.0, -2.0,  -1.0,
         1.0,  2.0,  1.0,
        -1.0,  2.0,  1.0,
         1.0, -2.0,  1.0,
        -1.0, -2.0,  1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    objVertexPositionBuffer.itemSize = 3;
    objVertexPositionBuffer.numItems = 8;
    objVertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, objVertexColorBuffer);
    var colors = [
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 0.5,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 0.5,
        1.0, 0.0, 0.0, 1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
    objVertexColorBuffer.itemSize = 4;
    objVertexColorBuffer.numItems = 8;
}
var iObjDeg = 0;
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, -10.0]);
    mvPushMatrix();
    mat4.rotate(mvMatrix, degToRad(iObjDeg), [1, 1, 1]);
    gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, objVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, objVertexColorBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, objVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, objVertexPositionBuffer.numItems);
    mvPopMatrix();
}
function drawFrame() {
    requestAnimFrame(drawFrame);
    drawScene();
    iObjDeg += 3; // 3 degrees per frame
}
function initWebGl() {
    var canvas = document.getElementById('panel');
    initGL(canvas);
    initShaders()
    initObjBuffers();
    gl.clearColor(1.0, 0.5, 0.3, 1.0);
    gl.enable(gl.DEPTH_TEST);
    drawFrame();
}

Pretty big code, isn’t it? but anyway – here are all necessary to draw our demo. Since page loaded – we performing few initializations (initGL, initShaders, initObjBuffers functions), then we defining color for our scene (via gl.clearColor), and after – drawing our scene (drawFrame function). In this function we using ‘requestAnimFrame’ to define function which will calling periodically when WebGL will need to draw next frame.


Live Demo

Conclusion

Not bad, isn`t it? Possible today we made first step to next generation. And possible that in coming future we will start a series of articles related with game development with HTML5. Welcome back!

SIMILAR ARTICLES

Understanding Closures

0 22905

1 COMMENT

Leave a Reply