13 14 15 16

WebGL class

9

We've learned many things so far, and our code examples have quite grown by this point. Before we continue, let's organize our code in the object-oriented style so we could keep things neat and clean. We will update this class from time to time after we learn new things in WebGL. But as for now, it has the following functions:

  • createProgram — create GLSL program from the given source code;
  • createTexture — create WebGL texture from the given image URL. Set up the texture and initialize it in onload handler;
  • createBuffer — create vertex buffer, fill it with the given data, and bind topology and vertex attributes information to it;
  • drawArrays — draw geometry from the given buffer and GLSL program;
  • draw — rendering callback wrapper which is bound to WebGL class instance and do requestAnimationFrame stuff;
  • context — read-only access to the underlying WebGL context.

Full code of our brand new WebGL class:

var WebGL = function(canvas)
{
    const gl = canvas.getContext("webgl2");

    //  Access WebGL context
    this.context = function()
    {
        return gl;
    };

    // Create GLSL program from given sources, compile and link
    this.createProgram = function(vertexProgram, fragmentProgram)
    {
        const vs = gl.createShader(gl.VERTEX_SHADER);
        const fs = gl.createShader(gl.FRAGMENT_SHADER);
        const program = gl.createProgram();

        compile(vs, vertexProgram);
        compile(fs, fragmentProgram);

        gl.attachShader(program, vs);
        gl.attachShader(program, fs);
        gl.linkProgram(program);

        if(!gl.getProgramParameter(program, gl.LINK_STATUS))
        {
            alert(gl.getProgramInfoLog(program));
        }

        return program;
    };

    // Create texture from the given image source URL
    this.createTexture = function(src)
    {
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

        const image = new Image();
        image.src = src;
        image.onload = function()
        {
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this);
            gl.generateMipmap(gl.TEXTURE_2D);
        };

        return texture;
    };

    // Create vertex buffer with the given topology and vertex attributes
    this.createBuffer = function(data, topology, attributes)
    {
        const buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
        buffer.topology = topology;
        buffer.attributes = attributes;
        buffer.count = data.length / valuesPerVertex(attributes);

        return buffer;
    }

    // Draw geometry from the given buffer with the given GLSL program
    this.drawArrays = function(program, buffer)
    {
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

        const stride = valuesPerVertex(buffer.attributes) * Float32Array.BYTES_PER_ELEMENT;
        let offset = 0;

        for(const name in buffer.attributes)
        {
            const size = buffer.attributes[name];
            const location = gl.getAttribLocation(program, name);

            gl.enableVertexAttribArray(location);
            gl.vertexAttribPointer(location, size, gl.FLOAT, false, stride, offset);

            offset += size * Float32Array.BYTES_PER_ELEMENT;
        }

        gl.drawArrays(buffer.topology, 0, buffer.count);
    };

    // Rendering callback wrapper with binding to WebGL class instance
    this.draw = function(callback)
    {
        callback = callback.bind(this);

        const draw = function(timestamp)
        {
            callback(timestamp);
            window.requestAnimationFrame(draw);
        };

        window.requestAnimationFrame(draw);
    };

    // Count quantity of values of a single vertex
    function valuesPerVertex(attributes)
    {
        let values = 0;

        for(const name in attributes)
        {
            values += attributes[name];
        }

        return values;
    }

    // Compile the given shader and check for errors
    function compile(shader, source)
    {
        gl.shaderSource(shader, source);
        gl.compileShader(shader);

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

So our previous “Textures” lesson can be rewritten in a cleaner way:

<script src="webgl.js"></script>
<script type="text/javascript">
    function text(id)
    {
        return document.getElementById(id).value;
    }

    const wgl = new WebGL(document.getElementById("canvas"));
    const gl = wgl.context();
    const program = wgl.createProgram(text("vs"), text("fs"));
    const texture = wgl.createTexture("texture.jpg");

    const vertices =
    [
        -1, -1, 0, 0,
        -1,  1, 0, 1,
         1, -1, 1, 0,
         1,  1, 1, 1
    ];

    const buffer = wgl.createBuffer(new Float32Array(vertices), gl.TRIANGLE_STRIP,
    {
        position : 2,
        texCoord : 2
    });

    const aspect = gl.getUniformLocation(program, "aspect");
    const time = gl.getUniformLocation(program, "time");
    const diffuse = gl.getUniformLocation(program, "diffuse");

    wgl.draw(function(timestamp)
    {
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.useProgram(program);
        gl.uniform1f(aspect, canvas.height / canvas.width);
        gl.uniform1f(time, timestamp / 1000);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.uniform1i(diffuse, 0);

        wgl.drawArrays(program, buffer);
    });
</script>

Now we can easily distinguish how things are initialized and how they're used. Full source code for this lesson: source.zip

Lesson 15
Share this page:

Learning plan

How to evaluate a vertex-specified data and interpolate it between vertices
14. Textures
An introduction on how to load texture, initialize it properly with WebGL and pass it into GLSL program
15. glMatrix
How to install and use glMatrix library which provides vector and matrix arithmetics and helper functions
16. WebGL class
We've learned many thing by this point. Let's start organizing them neat and clean in the object-oriented way