Creating an Animated 3D WebGL Demonstration

Tutorials

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

01 <!DOCTYPE html>
02 <html lang="en" >
03 <head>
04     <link href="css/main.css" rel="stylesheet" type="text/css" />
05     <script type="text/javascript" src="js/glMatrix-0.9.5.min.js"></script>
06     <script type="text/javascript" src="js/webgl-utils.js"></script>
07     <script type="text/javascript" src="js/script.js"></script>
08     <title>WebGL demonstration | Script Tutorials</title>
09 </head>
10 <body onload="initWebGl();">
11     <div class="example">
12         <h3><a href="https://www.script-tutorials.com/making-3d-webgl-demonstration/">WebGL demonstration | Script Tutorials</a></h3>
13         <canvas id="panel" width="500" height="333"></canvas>
14         <div style="clear:both;"></div>
15     </div>
16 </body>
17 </html>

Step 2. CSS

Here are used CSS styles.

css/main.css

01 body {
02     background:#eee;
03     font-family:VerdanaHelveticaArialsans-serif;
04     margin:0;
05     padding:0
06 }
07 .example {
08     background:#fff;
09     width:500px;
10     font-size:80%;
11     border:1px #000 solid;
12     margin:20px auto;
13     padding:15px;
14     position:relative;
15     -moz-border-radius: 3px;
16     -webkit-border-radius:3px
17 }
18 h3 {
19     text-align:center;
20 }

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 function initGL(canvas) {
004     try {
005         gl = canvas.getContext('experimental-webgl');
006         gl.viewportWidth = canvas.width;
007         gl.viewportHeight = canvas.height;
008     catch (e) {}
009     if (! gl) {
010         alert('Can`t initialise WebGL, not supported');
011     }
012 }
013 function getShader(gl, type) {
014     var str = '';
015     var shader;
016     if (type == 'x-fragment') {
017         str = "#ifdef GL_ES\n"+
018 "precision highp float;\n"+
019 "#endif\n"+
020 "varying vec4 vColor;\n"+
021 "void main(void) {\n"+
022 "    gl_FragColor = vColor;\n"+
023 "}\n";
024         shader = gl.createShader(gl.FRAGMENT_SHADER);
025     else if (type == 'x-vertex') {
026         str = "attribute vec3 aVertexPosition;\n"+
027 "attribute vec4 aVertexColor;\n"+
028 "uniform mat4 uMVMatrix;\n"+
029 "uniform mat4 uPMatrix;\n"+
030 "varying vec4 vColor;\n"+
031 "void main(void) {\n"+
032 "    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n"+
033 "    vColor = aVertexColor;\n"+
034 "}\n";
035         shader = gl.createShader(gl.VERTEX_SHADER);
036     else {
037         return null;
038     }
039     gl.shaderSource(shader, str);
040     gl.compileShader(shader);
041     if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
042         alert(gl.getShaderInfoLog(shader));
043         return null;
044     }
045     return shader;
046 }
047 function initShaders() {
048     var fragmentShader = getShader(gl, 'x-fragment');
049     var vertexShader = getShader(gl, 'x-vertex');
050     shaderProgram = gl.createProgram();
051     gl.attachShader(shaderProgram, vertexShader);
052     gl.attachShader(shaderProgram, fragmentShader);
053     gl.linkProgram(shaderProgram);
054     if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
055         alert('Can`t initialise shaders');
056     }
057     gl.useProgram(shaderProgram);
058     shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
059     gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
060     shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, 'aVertexColor');
061     gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
062     shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, 'uPMatrix');
063     shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMVMatrix');
064 }
065 var mvMatrix = mat4.create();
066 var mvMatrixStack = [];
067 var pMatrix = mat4.create();
068 function mvPushMatrix() {
069     var copy = mat4.create();
070     mat4.set(mvMatrix, copy);
071     mvMatrixStack.push(copy);
072 }
073 function mvPopMatrix() {
074     if (mvMatrixStack.length == 0) {
075         throw 'Invalid popMatrix!';
076     }
077     mvMatrix = mvMatrixStack.pop();
078 }
079 function setMatrixUniforms() {
080     gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
081     gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
082 }
083 function degToRad(degrees) {
084     return degrees * Math.PI / 180;
085 }
086 var objVertexPositionBuffer;
087 var objVertexColorBuffer;
088 function initObjBuffers() {
089     objVertexPositionBuffer = gl.createBuffer();
090     gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer);
091     var vertices = [
092          1.0,  2.0,  -1.0,
093         -1.0,  2.0,  -1.0,
094          1.0, -2.0,  -1.0,
095         -1.0, -2.0,  -1.0,
096          1.0,  2.0,  1.0,
097         -1.0,  2.0,  1.0,
098          1.0, -2.0,  1.0,
099         -1.0, -2.0,  1.0,
100     ];
101     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
102     objVertexPositionBuffer.itemSize = 3;
103     objVertexPositionBuffer.numItems = 8;
104     objVertexColorBuffer = gl.createBuffer();
105     gl.bindBuffer(gl.ARRAY_BUFFER, objVertexColorBuffer);
106     var colors = [
107         1.0, 0.0, 0.0, 1.0,
108         0.0, 1.0, 0.0, 0.5,
109         0.0, 1.0, 0.0, 1.0,
110         0.0, 0.0, 1.0, 1.0,
111         0.0, 0.0, 1.0, 1.0,
112         0.0, 1.0, 0.0, 1.0,
113         0.0, 1.0, 0.0, 0.5,
114         1.0, 0.0, 0.0, 1.0,
115     ];
116     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
117     objVertexColorBuffer.itemSize = 4;
118     objVertexColorBuffer.numItems = 8;
119 }
120 var iObjDeg = 0;
121 function drawScene() {
122     gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
123     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
124     mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
125     mat4.identity(mvMatrix);
126     mat4.translate(mvMatrix, [0.0, 0.0, -10.0]);
127     mvPushMatrix();
128     mat4.rotate(mvMatrix, degToRad(iObjDeg), [1, 1, 1]);
129     gl.bindBuffer(gl.ARRAY_BUFFER, objVertexPositionBuffer);
130     gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, objVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
131     gl.bindBuffer(gl.ARRAY_BUFFER, objVertexColorBuffer);
132     gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, objVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
133     setMatrixUniforms();
134     gl.drawArrays(gl.TRIANGLE_STRIP, 0, objVertexPositionBuffer.numItems);
135     mvPopMatrix();
136 }
137 function drawFrame() {
138     requestAnimFrame(drawFrame);
139     drawScene();
140     iObjDeg += 3; // 3 degrees per frame
141 }
142 function initWebGl() {
143     var canvas = document.getElementById('panel');
144     initGL(canvas);
145     initShaders()
146     initObjBuffers();
147     gl.clearColor(1.0, 0.5, 0.3, 1.0);
148     gl.enable(gl.DEPTH_TEST);
149     drawFrame();
150 }

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!

Rate article