WebGL Overview

←Back

WebGL Introduction

WebGL is a JavaScript API based on OpenGL ES (Embedded System) to render 2D or 3D graphics on a HTML web page using <canvas> element. It is one of core features in HTML5 standard since 2008.

WebGL provides JavaScript objects, functions and data types equivalent to OpenGL.

One of the advantages of using WebGL is easier to create a GUI application compared to OpenGL | ES. You can directly use the built-in UI elements in the same web page, even on the top of the canvas element.

Version Comparison
APIEquivalent Version
OpenGL1.32.03.0
OpenGL ES1.02.03.0
WebGL1.02.0

Rendering Context (RC)

Before drawing something to a <canvas> tag, you must create a WebGL rendering context (RC) first from the <canvas>. RC is a WebGL running environment to store all WebGL states and to pass function calls through this.

You can get WebGL RC using HTMLCanvasElement.getContext() with either "webgl" or "webgl2" context type. getContext() returns WebGLRenderingContext or WebGL2RenderingContext object depending on the input type. These RC objects contains WebGL states and functions that require for WebGL rendering process.


// in HTML
<canvas id="webglView"></canvas>
...

// in JavaScript
let canvas = document.getElementById("webglView"); // get DOM element
let gl = canvas.getContext("webgl2");              // get WebGL 2 RC object
let version = gl.getParameter(gl.VERSION);         // get WebGL version string from RC

The above code tries to get WebGL 2.0 rendering context. If the web browser doesn't support version 2 yet, you can fall back version 1 instead. Please see the getContextGL() in webglUtils.js for detail.

WebGL Initialization

WebGL is a state machine. Drawing modes and attributes need to initialized before drawing. Most states can be enabled or disabled with gl.enable() and gl.disable() functions. You can also check the current state using gl.isEnabled().

A common initialzation process is to set the background colour, enable depth test and backface culling. The following is an example of WebGL initialization.


function initGL(gl)
{
    gl.clearColor(1.0, 1.0, 1.0, 0.0);
    gl.clearDepth(1.0);
    gl.enable(gl.DEPTH_TEST);   // enable depth test
    gl.depthFunc(gl.LEQUAL);
    gl.enable(gl.CULL_FACE);    // enable culling backface
    gl.cullFace(gl.BACK);
    gl.enable(gl.BLEND);        // enable blend
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    ...
}

Rendering Loop

In order to animate or redraw the framebuffer continuously in WebGL, you need a separate rendering loop without user intervention. requestAnimationFrame() function is the best option for the rendering loop. This method is optimized for battery life and performance, and is synchronized with the display refresh rates.


// get WebGL RC object and init WebGL 
let gl = canvas.getContext("webgl2");
...

// start rendering loop after WebGL is initialized
startRendering(gl);

function startRendering(gl)
{
    // define rendering callback for single frame
    let frameCallback = () =>
    {
        preFrame();
        frame();
        postFrame();
        requestAnimationFrame(frameCallback); // subsequent calls
    };

    // initial call
    requestAnimationFrame(frameCallback);
}

function preFrame(gl)
{
    // perform time-sensitive tasks before drawing, such as measuring frame time
}
function frame(gl)
{
    // draw a single frame to framebuffer
}
function postFrame(gl)
{
    // perform time-insensitive tasks after drawing
}

Example

This example is a simplest WebGL application to refresh the background colour by cycling through the colour spectrum. It doesn't require any GLSL shader nor drawing 3D geometry to the screen.


// minimal JS implementation
// See the full JS code from the download link

// global var
gl = {};

// get WebGL RC from canvas
let canvas = document.getElementById("webglView");
gl = canvas.getContext("webgl2");

// initial bg color
gl.bgColor = {r:0, g:0, b:0};
gl.hue = 0; // red

// start rendering loop
startRendering(gl);

function startRendering(gl)
{
    let frameCallback = () =>
    {
        frame();
        postFrame();
        requestAnimationFrame(frameCallback);
    };
    requestAnimationFrame(frameCallback);
}

function frame()
{
    // clear framebuffer
    gl.clearColor(gl.bgColor.r, gl.bgColor.g, gl.bgColor.b, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

function postFrame()
{
    // update hue to rgb
    gl.hue++; // 0~360
    if(gl.hue >= 360) gl.hue = 0;
    let h = (gl.hue / 60) % 6; // 0~360 to 0~6
    if(h < 1)      gl.bgColor = {r:1,   g:h,   b:0};
    else if(h < 2) gl.bgColor = {r:2-h, g:1,   b:0};
    else if(h < 3) gl.bgColor = {r:0,   g:1,   b:h-2};
    else if(h < 4) gl.bgColor = {r:0,   g:4-h, b:1};
    else if(h < 5) gl.bgColor = {r:h-4, g:0,   b:1};
    else           gl.bgColor = {r:1,   g:0,   b:6-h};
}


decoration Fullscreen Demo: test_init.html
decoration GitHub Repo: test_init.html

←Back
 
Hide Comments
comments powered by Disqus