WebGL Overview
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.
API | Equivalent Version | ||
---|---|---|---|
OpenGL | 1.3 | 2.0 | 3.0 |
OpenGL ES | 1.0 | 2.0 | 3.0 |
WebGL | 1.0 | 2.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};
}
Fullscreen Demo: test_init.html
GitHub Repo: test_init.html