WebGL Essentials

WebGL Rendering Context

The 2D Context for the HTML canvas element exposes a set of properties and methods quite efficient for creating vivid two-dimensional scenes. Combining the canvas API with script-based animations and DOM event handlers lays the basis for interactive apps and online games. WebGL has become a legitimate extension of the canvas functionality: building 3D worlds in the browser has forwarded the Web to the next level of development.

WebGL 1.0 is an implementation of Open Graphics Library conforming to the OpenGL ES 2.0 API. In its essence, OpenGL is just a software interface to graphics hardware. The interface consists of numerous commands specifying operations necessary to draw color images of three-dimensional objects. OpenGL for Embedded Systems, or OpenGL ES, is a subset of the 3D API designed for hardware running on embedded and mobile devices. In the series of articles we will note down the principles of WebGL programming and compare browser-based rendering with 3D routines expressed in Java and C.

A WebGL application starts with acquiring a WebGL context:

var canvas = document.querySelector('canvas');
var gl = canvas.getContext('webgl');

The getContext() method of the canvas with the webgl argument returns an instance of WebGLRenderingContext. The object is used to manage OpenGL state and render to the drawing buffer.

The presumed size of the buffer is determined by the width and height of the canvas element. The actual dimensions are retrieved by getting the values of the drawingBufferWidth and drawingBufferHeight properties:

console.log(gl.drawingBufferWidth);
console.log(gl.drawingBufferHeight);

Color, depth and stencil buffers are constituent parts of the drawing buffer. A color is defined as four components (red, green, blue and alpha). The minimum number of bits per a component is 8:

console.info('Number of bits per R component: %c%s', 'color: red', gl.getParameter(gl.RED_BITS));
console.info('Number of bits per G component: %c%s', 'color: lime', gl.getParameter(gl.GREEN_BITS));
console.info('Number of bits per B component: %c%s', 'color: blue', gl.getParameter(gl.BLUE_BITS));
console.info('Number of bits per A component: %c%s', 'color: silver', gl.getParameter(gl.ALPHA_BITS));

The getParameter() method in the example above is a substitute for OpenGL functions querying the system for current values of state variables. A code snippet in C obtaining the number of red, green, blue and alpha bitplanes in the color buffer might look like this:

int r[1], g[1], b[1], a[1];
glGetIntegerv(GL_RED_BITS, r);
glGetIntegerv(GL_GREEN_BITS, g);
glGetIntegerv(GL_BLUE_BITS, b);
glGetIntegerv(GL_ALPHA_BITS, a);
printf("RED: %d bits\nGREEN: %d bits\nBLUE: %d bits\nALPHA: %d bits", r[0], g[0], b[0], a[0]);

WebGL Context Attributes

An alpha channel as well as depth and stencil buffers can be switched on/off on demand: a valid WebGL context has a set of context attributes defining the characteristics of the drawing surface. To retrieve them, the getContextAttributes() method should be called:

console.dir(gl.getContextAttributes());

A current implementation of WebGL in a flagship browser will produce the following list of attributes and their default values:

alpha: true
antialias: true
depth: true
premultipliedAlpha: true
preserveDrawingBuffer: false
stencil: false

The alpha attribute enables an alpha channel for compositing and blending operations. If the premultipliedAlpha is true, then the HTML page compositor will assume that the drawing buffer contains colors with premultiplied alpha.

The antialias attribute switches on antialiasing. This is a special technique for rendering smoother graphical objects. Without antialiasing, edges of lines and curves can appear jagged. Aliasing takes place because a graphics primitive is approximated by turning on the pixels that are closest to its path. If antialiasing is enabled, the intensity of pixels surrounding the path is set in proportion to the amount of area covered by the primitive.

The depth buffer is indispensable for 3D scenes. The buffer associates a depth value with each pixel on the canvas plane. The value is usually encoded with 24 bits:

console.info('Number of bits per pixel in the depth buffer: %c%s', 'color: brown', gl.getParameter(gl.DEPTH_BITS));

The stencil buffer is disabled by default. Stencil manipulations can restrict drawing to certain portions of the canvas. The code below switches on stenciling:

var gl = canvas.getContext('webgl', {
 stencil: true
});
// 8 bits
console.info('Number of bits per pixel in the stencil buffer: %c%s', 'color: brown', gl.getParameter(gl.STENCIL_BITS));

The preserveDrawingBuffer attribute affects the contents of the drawing buffer: if the attribute is false, bitplanes in the color, depth and stencil buffers are cleared to their default values. These values for the color are derived from the transparent black (0, 0, 0, 0), the depth value is 1.0, and the stencil buffer is filled with 0.

WebGL Extensions

The OpenGL standard permits software and hardware vendors to add supplementary functions to the core commands. New routines are made available to developers through OpenGL extensions. In a C program conforming to the OpenGL ES 2.0 specification the list of extensions is obtained by calling the glGetString():

printf("%s", glGetString(GL_EXTENSIONS));

In WebGL an array of extension names is returned by the getSupportedExtensions() method:

console.log(gl.getSupportedExtensions().join('\n'));

The getExtension() method creates an object with constants and functions necessary for a requested extension:

var extension = gl.getExtension('OES_vertex_array_object');
// functions for working with vertex array objects
console.log(typeof extension.createVertexArrayOES);
console.log(typeof extension.bindVertexArrayOES);
console.log(typeof extension.deleteVertexArrayOES);