A WebGL Primer

Web Graphics Library, or WebGL for short, is a JavaScript API for rendering interactive 2D or 3D graphics in the browser (those browsers that support it). It is maintained by the Khronos Group which has this to say about WebGL on it’s website.

WebGL is a cross-platform, royalty-free web standard for a low-level 3D graphics API based on OpenGL ES 2.0, exposed through the HTML5 Canvas element as Document Object Model interfaces…WebGL brings plugin-free 3D to the web, implemented right into the browser. Major browser vendors Apple (Safari), Google (Chrome), Mozilla (Firefox), and Opera (Opera) are members of the WebGL Working Group.

What this looks like practically is that a developer can use JavaScript to draw graphics on the HTML <canavas> element. The end result brings a significant change to how developers go about rendering this sort of content on websites.

In the early to mid-2000’s anything having to do with 2D and 3D graphical animation and/or interactivity on the web was done in Flash. Flash was used to build entire websites that assaulted the senses with a rich, dynamic smorgasbord of visual and audio presentations. However, when Apple decided **not** to support Flash on it’s increasingly popular iPhone (and later the iPad) coupled with other problems inherent in Flash — such as its propensity to be a CPU and resource hog and the inability for search engines to index the content within it — developers began to move away from it. HTML5 emerged and an increase in the popularity of using JavaScript to provide rich interactive experiences on web sites came along with it. WebGL is a part of that transition.

Both 2D and especially 3D graphics are a difficult thing to master programatically. There is just a lot of math that ends up being involved and unless you’re the type of person who enjoys math and is good at it, animating and rendering graphics can be a rather daunting task. Look at the following code that draws a simple triangle to a canvas…

<!doctype html>
<head>
<title>Web GL</title>
</head>
<body>
<canvas width="500" height="500" id="mainCanvas"></canvas>

<script>
function main()
{
    var gl;
    var canvas = document.getElementById('mainCanvas');
    try {
        gl = canvas.getContext('experimental-webgl');
    } catch (e) {
        throw new Error('no WebGL found');
    }

    var vertices = [
      0.0, 0.5,
      0.5,  -0.5,
      -0.5, -0.5,
    ];
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

    var vertCode =
        'attribute vec2 coordinates;' +
        'void main(void) {' +
        '  gl_Position = vec4(coordinates, 0.0, 1.0);' +
        '}';

    var vertShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertShader, vertCode);
    gl.compileShader(vertShader);
    if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS))
      throw new Error(gl.getShaderInfoLog(vertShader));

    var fragCode =
      'void main(void) {' +
      '   gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);' +
     '}';

    var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragShader, fragCode);
    gl.compileShader(fragShader);
    if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS))
         throw new Error(gl.getShaderInfoLog(fragShader));

    var shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertShader);
    gl.attachShader(shaderProgram, fragShader);
    gl.linkProgram(shaderProgram);
    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
        throw new Error(gl.getProgramInfoLog(shaderProgram));

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.useProgram(shaderProgram);
    var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates");
    gl.enableVertexAttribArray(coordinatesVar);
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.vertexAttribPointer(coordinatesVar, 2, gl.FLOAT, false, 0, 0);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
}

window.onload = main;
</script>
</body>
</html>

Ugh! Thanks, but no thanks. If this much code is required to draw a simple triangle then you can only imagine what rendering more complex objects would be like… let alone animating them and/or adding event handlers for mouse actions. On the face of it, WebGL looks pretty painful and arduous.

Fortunately, though, there are some good libraries out there written in JavaScript that abstract away a lot of the gruntwork involved in using WebGL. We can use these libraries and pass a number of different configuring parameters into them and often achieve the same result with a lot less code. What’s even better is that a lot of these libraries are free and open source! You gotta love the Internet and the open source community!

Three.js is probably the best known WebGL library. Some of the examples show what can be accomplished by using it. It has also been used in some of the Chrome demos such as 100,000 stars put out by Google. What’s even better is that the code is freely available on GitHub and can be used by anyone who wants to use it.

To give you a perspective on the usefulness of using a WebGL library like Three.js, below is the code to render the the same triangle that we created earlier only this time drawn using Three.js…

<!doctype html>
<head>
<title>Web GL</title>
<script src="js/three.js"></script>
</head>
<body>

<script>
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);                    
 
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize(500, 500);
    renderer.setClearColor(0x000000, 1);
    document.body.appendChild(renderer.domElement);
                 
    var triangle = new THREE.Geometry();            
    triangle.vertices.push( new THREE.Vector3( 0, 20, 0 ));
    triangle.vertices.push( new THREE.Vector3( -20, -20, 0 )); 
    triangle.vertices.push( new THREE.Vector3( 20, -20, 0 ));  
    triangle.faces.push( new THREE.Face3( 0, 1, 2 ) );                  
                            
    var material = new THREE.MeshBasicMaterial({color: 0xffffff});   
    var triangleMesh = new THREE.Mesh( triangle, material );
    triangleMesh.position.x = 0;       
    scene.add(triangleMesh);       
    camera.position.z = 50;      
    renderer.render(scene, camera);

</script>
</body>
</html>

While there is definitely still some code to sift through and understand, it is nowhere near as cumbersome as what we run into with native WebGL.

We will take a deeper look at Three.js and possibly some other WebGL libraries in upcoming discussions… including different appoaches to adding some interactivity using events like mouse clicks and hovers and also how you can draw some scenes that are more complex than a simple triangle. For now, we just wanted to give a good simple introductory overview on what WebGL is and where and how it can be used. When you’re looking at a subject as involved as 3D graphics — something that requires a considerable amount of thought — it’s probably best not to take a drink from the fire hydrant and absorb too much all at once. 😉

, 9bit Studios E-Books

Like this post? How about a share?

Stay Updated with the 9bit Studios Newsletter

0 Responses to A WebGL Primer

    Leave a Reply

    Your email address will not be published. Required fields are marked *