WebGL (Abstraction) for All

James Milner

@JamesLMilner

loxodrome.io

WebGL?

WebGL is a library for displaying graphics in the browser

It Uses HTML5 Canvas

Why WebGL?

But...

Greg Tavares

"what many people don't [realise] is that WebGL is a rasterization API, not a 3D API"

It's API is also low level

Alongside our WebGL JavaScript API we require shaders 

Vertex Shaders, Fragment Shaders

Introducing GLSL ES

Graphics Library Shader Language

<!-- Drawing a green square -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;
     
    void main() {
      gl_Position = vec4(a_position, 0, 1); // X, Y, Z, W
    }
</script>
 
<script id="2d-fragment-shader" type="x-shader/x-fragment">
    void main() {
      gl_FragColor = vec4(0, 1, 0, 1);  //  Green (RGBA)
    }
</script>

Credit: dev.opera.com

Lets Examine Some Code...

<html>

<head>
<title>Learning WebGL — lesson 1</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<script type="text/javascript" src="glMatrix-0.9.5.min.js"></script>

<script id="shader-fs" type="x-shader/x-fragment">
    precision mediump float;

    void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
</script>

<script id="shader-vs" type="x-shader/x-vertex">
    attribute vec3 aVertexPosition;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;

    void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    }
</script>


<script type="text/javascript">

    var gl;
    function initGL(canvas) {
        try {
            var gl = canvas.getContext("experimental-webgl");
            if (!gl) {
              gl = canvas.getContext("webgl");
            }
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }


    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null; // No shader script with that id
        }

        var str = ""; //concatenate the whole shader together
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) { //text will have nodeType property will return 3.
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER); // if fragment
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER); // if vertex
        } else {
            return null;
        }

        gl.shaderSource(shader, str); // link type and source
        gl.compileShader(shader); // compile shader 

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }


    var shaderProgram;

    function initShaders() {
        var fragmentShader = getShader(gl, "shader-fs");
        var vertexShader = getShader(gl, "shader-vs");

        shaderProgram = gl.createProgram(); // create shader program
        gl.attachShader(shaderProgram, vertexShader); // add vertex shader
        gl.attachShader(shaderProgram, fragmentShader); // add vertex shader
        gl.linkProgram(shaderProgram); // Link the two to program

        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(shaderProgram); 

        shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

        shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
        shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    }


    var mvMatrix = mat4.create(); // glMatrix functions
    var pMatrix = mat4.create();

    function setMatrixUniforms() {
        gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
        gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
    }


    var triangleVertexPositionBuffer;
    var squareVertexPositionBuffer;

    function initBuffers() {

        //Triangle
        triangleVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
        var vertices = [
             0.0,  1.0,  0.0,
            -1.0, -1.0,  0.1,
             1.0, -1.0,  0.0
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        triangleVertexPositionBuffer.itemSize = 3;
        triangleVertexPositionBuffer.numItems = 3;

        //Square
        squareVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        vertices = [
             1.0,  1.0,  0.0,
            -1.0,  1.0,  0.0,
             1.0, -1.0,  0.0,
            -1.0, -1.0,  0.0
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        squareVertexPositionBuffer.itemSize = 3;
        squareVertexPositionBuffer.numItems = 4;
    }


    function drawScene() {
        gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // Clears the color and depth buffers

        mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
        // Field of View = 45 , width-to-height ratio of canvas, min view distance, max view distance
        mat4.identity(mvMatrix);

        mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);
        gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
        setMatrixUniforms();
        gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

        mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
        setMatrixUniforms();
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
    }



    function webGLStart() {
        var canvas = document.getElementById("lesson01-canvas"); //Get Canvas
        initGL(canvas);
        initShaders();
        initBuffers();

        gl.clearColor(0.0, 0.0, 0.0, 1.0); //Black
        gl.enable(gl.DEPTH_TEST);

        drawScene();
    }


</script>


</head>


<body onload="webGLStart();">
    <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br />

    <canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>

    <br/>
    <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br />
</body>

</html>

Frameworks will help us move faster

Raw WebGL

TWGL

Three.js

Babylon.js

Scene.js

OSG.js

GLGE

Increasing Abstraction

CopperLicht

PhiloGL

Which is best?

It depends 

I like Three.js

WebGL & libraries have great potential & use cases

 

 

Here's some examples of places I've ended up using it

WebGL in Academia

Lacuna: a 3D Web GIS

WebGL for Fun

WebGL in my Day Job

Take Aways

  • WebGL is great but has a low level API
  • Abstraction libraries help here 
  • Give a library a try, build something cool
  • Appreciate feedback on our latest JS API!

Thanks

WebGL (Abstraction) for All

By James Milner

WebGL (Abstraction) for All

How to leverage abstraction libraries for WebGL for fun, knowledge and profit.

  • 1,582