WebGL Quick Start

WebGL Book: A WebGL Tutorial Reference Book
Easily understand WebGL and learn how to create 3D worlds and games from scratch.

WebGL Gems is the authorative guide on WebGL programming published by Learning Curve - education for software developers. This book in JavaScript programming series clearly, and in easy to follow language, explains fundamental principles behind programming 3D games using the WebGL framework for JavaScript.

Preorder Now in PDF format for Kindle and get a discount.
WebGL Gems is coming out on April 15th, 2017.
The paperback version will be available via this Amazon page, please bookmark it!

Clearing Canvas (And a few other things)

We've already initialized 3D canvas and talked a bit about theory behind the WebGL pipeline. I think it's a good time to actually do something physical on the screen.

Let's use the very basic of GL commands on the newly created canvas. We won't do much in this section other than familiarize ourselves with a few basic WebGL functions.

If you've ever programmed in OpenGL before, they have synonymous names. Except in WebGL they stem from the main context object (in this example it is called gl) as its methods.

This example assumes that jQuery library is included providing $(document).ready function which executes at the time the page's DOM has finished downloading.

But you can simply write this code at the bottom of your page just before closing </body> tag or execute these commands from <body onload = "here"> if you don't want to use jQuery.

jQuery is that kind of library that some programmers don't like because it adds an extra HTTP request to your page. And you won't be using most of its functions or methods. But it is exceptionally great for determining when DOM is loaded, without making a mess of your code.

For JavaScript Purists

Don't want to use jQuery? That's fine. You can simply attach the event to window.onload function as will be shown below.

Just rewrite the window's default onload function as follows. Remember that in JavaScript - thanks to a principle called hoisting - functions don't have to be defined first in order to be used.

In other words, we can define our function later, after we already attached it to an event in our code:

And for this reason we can do something like this:


    window.onload = InitializeWebGL;

    function InitializeWebGL() { /* Write your WebGL init code here */ }

jQuery accomplishes this a single statement. It's up to you which one you will use.

Here is the jQuery example with actual WebGL initialization code:


    // Execute this code only after DOM has finished loading completely
    $(document).ready(function()
    {
        var canvas = document.getElementById('gl');

        var gl = GetWebGLContext( canvas );

        if ( !gl ) {

            console.log('Failed to set up WebGL.');

        } else {

            // WebGL initialized!
            gl.clearColor(1.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);
        }
    });

If you supplied a buffer type that is not supported or does not exist, the result of your gl "clear" operation will produce the following error: INVALID_VALUE.

The function gl.clearColor accepts 4 parameters for the RGBA values. The RGB value in 0.0-1.0f format (where 0.0 = 0 and 1.0 = 255) for each one of the 3 color channels, followed by the fourth parameter specifying alpha value for translucency effect. In this case alpha was set to 255 to create a lush solid red color.

The color defined by clearColor is retained by the pipeline once it is set and you're not required to set it on every frame. It is changed only if clearColor function is used once again to reset it.

But it's the function gl.clear that is responsible for actually wiping the screen clean with the selected color. It takes gl.COLOR_BUFFER_BIT flag which says: clear the color component.

Basic Types of Memory Buffers

Color buffers are only one type of buffers in WebGL. And the flag COLOR_BUFFER_BIT represents simple [r, g, b] set. The other two flags are listed below.

COLOR_BUFFER_BIT Only pixel color will be read / written
DEPTH_BUFFER_BIT Operation will be performed on an off-screen depth buffer
STENCIL_BUFFER_BIT Same as above, except for stencil buffer

Below is an example of how you would use functions that relate to setting the rest of the buffers. For example, note that clearDepth and clearStencil are used to specify Depth and Stencil buffers. These buffers must be first created, but we'll take a look at how that's done later in the book when it becomes necessary:


    gl.clearColor( red, green, blue, alpha );
    gl.clearDepth( depth );
    gl.clearStencil( stencil );

The DEPTH_BUFFER_BIT represents the depth buffer and it is used for determining the distance between the 3D fragment and the camera / eye position. The depth buffer stores only the Z coordinate in 3D space for each pixel rendered on screen starting from the position of the camera in the world. The depth buffer does not store X and Y coordinates.

As we continue rendering polygons on the screen the data in the depth buffer is then compared with pixels drawn in the same physical location on the screen (if any.)

If there is a pixel "A" rendered in that same spot on the screen, the new incoming pixel "B" which happens to occupy the same space is tested for whether it is farther away or closer to the camera than pixel "A".

This process continues until all pixels in the scene are processed. The 2D depth buffer is built for each pixel on the 2D screen. Depth buffers are also often passed to shaders for aiding calculation for creating various effects that require knowing distance between the currently processed fragment and the camera.

Whichever pixel is closer to the camera takes precedence and is picked to be the one that will be actually drawn on the screen. All other pixels "behind it" are discarded.

This improves shader performance because we no longer need to render every single pixel that will appear at some [x= y=] position in screen space. Just ones that are the closest to the camera. This avoids performance loss from fragment overdraw.

Ideally, we would want to draw our 3D world and models in it using an algorithm that draws objects from back to front, imitating how an artist would paint on a physical sheet of canvas. The painter first draws the background scene and objects that are far away. On top of that, the artist then proceeds to draw objects that are closer, overdrawing background items.

However, that sounds good only until you realize that some polygons that are translated and rotated to occupy exactly the same volume of space cannot be always drawn using this technique without losing visual integrity. It makes sense because, which object would you choose to draw first if they shared the same pixel coordinates in 3D space?

In fact, if two objects are drawn in a way where their pixels will share exactly the same 3D location your program can experience something referred to as Z-buffer fighting.

This is when the shader is struggling to decide which pixel out of the two should be shown at that location. Often this creates a pixel flickering effect, or long jagged-edge stripes of pixels seemingly flipping on and off following a will unknown to us as shown in the diagram below:

error in depth buffer calculation during z-buffer test

Image courtesy Department of Mathematics and Computer Science at Hobart and William Smith Colleges (http://math.hws.edu/graphicsbook/c3/s1.html)

This happens not due to the fault in the depth algorithm itself but because of the fact that floating-point operations on the CPU have limitations. Unfortunately, there is not much we can do in this case. Good news is that in most cases, we don't really need to.

To avoid this artifact we usually want to avoid drawing objects occupying exactly the same place altogether. But this depth buffer technique is still critical in being used for accurately rasterizing polygons that overlap each other when they do not occupy the same space.

If we avoid the depth buffer test, some of your polygons may appear to be rendered in random order and intersect each other, creating severe visual artifacts.

As long as large majority of fragments occupy a unique location in the world coordinates, the z-buffer technique is more than efficient to eliminate all issues with polygon ordering.

For example, consider this geometric object consisting of several rectangles.

polygon render order

Image courtesy: "Glasnost" http://glasnost.itcarlow.ie/

When z-buffer ordering technique is applied, the object is rendered accurately (left hand side). Without it, the polygon rendering order is obscure, and the model is inaccurately drawn.

We'll deal with these cases in practice later on in the book. I just wanted to show you this now before we move on, in order to get an idea of how different types of off-screen buffers can be used in WebGL.


© 2017 Copyright WebGL Tutorials (webgltutorials.org)

All content and graphics on this website are the property of webgltutorials.org - please provide a back link when referencing on other sites.