Anton Gerdelan. 18 May 2015.
Web applications are attractive because they are an excellent way to share your work. All the user needs to do is open a web link. WebGL is a browser-implemented interface that gives web applications access to powerful hardware-accelerated rendering. No plug-ins or installation are required. WebGL is based on the mobile version of OpenGL; "OpenGL ES", which does not require the very latest graphics hardware, it's very portable, and will run on older desktops and most mobile devices as well. And, even better, you don't need to make a special build for each operating system. You will probably find WebGL to be several times faster to create programmes with than the desktop and mobile equivalents.
You will find yourself switching between several languages when writing WebGL software.
|HTML5||Write a web page with canvas rectangle to render in.|
We can start by making a very simple HTML web-page.
The basic concept with WebGL software is that we write a normal web-page, and use the new HTML5 canvas tag to define an area of the page (a blank rectangle) that our rendering will draw to. You can use any of HTML's forms, text areas, and other elements to interact with your visualisation - we can say that we get a user interface library built-in.
I started with something like this:
Next, I ask the canvas to set itself up with a new WebGL context, and keep track of this as an object called gl. This object will be our interface to all of the WebGL functions.
My final instructions use the gl object to call GL functions. They are almost identical to OpenGL function names and constants, except that gl and GL_ are removed, and we access each through our new object. You can set the aplha channel here to 0.0 if you want to background to be transparent and the page background to show through. If you refresh the web page you should see the canvas bigger, and coloured. You can find the complete list of OpenGL functions on the WebGL Quick Reference card at https://www.khronos.org/webgl/.
You'll notice, reading the reference card, that WebGL does not support newer OpenGL's Vertex Array Object (VAO). It's actually quite tedious rendering in OpenGL without VAOs, as you have to set up vertex attribute pointers every time that you draw. There's a VAO extension for WebGL though. You can see it in the extension registry. We can query that in our script block, which will return a new object, which is then our interface to the VAO extension's subset of functions:
If it isn't supported by the user's browser/system then we can use the error logging mechanism of the browser console to report that. This also includes a line number link in the console's output.
Because the modern web uses an asynchronous download model we can never be sure when a resource will actually be downloaded. On a bad connection the onload function may not even execute for a minute after your main loop starts. In the mean-time we will at least have a valid, but empty, texture with a flag to check against. In a C programme that was sophisticated enough to load files asynchronously, we would have a little set-up like this:
You can see why it's useful to inject some state attributes, and where the "popping-in" effect of resources we see in WebGL demos comes from. If you want to avoid this odd effect you could have a function that checks if all of the assets are loaded before rendering, and instead render perhaps some "loading.." text, but from my experience with a larger commercial project which had a lot of resource data to download, you can get a very bad user experience if they have to wait for a long time on a mobile device or on a poor connection - you'll probably find users prefer "popping-in" to a long wait.
We also have varying instead of in here, and the built-in gl_FragColor instead of an output variable. WebGL fragment shaders are required to have that exact precision statement at the top.
I just use the DOM again to fetch the strings. Instead of a script block, you could just as well put your shaders in a visible textarea, and live edit them. Web elements have a range of .on....() functions that you can define. When a user clicks on something, or text is changed a callback function will fire - your function could recompile the shaders.
Following this, I have regular-looking code compiling the shaders and getting some variables to hold the locations of uniforms. Note that I also explicitly bind the locations of my attributes to 0, 1, and 2, for points, texture coordinates, and normals, respectively.
"PV" is my combined projection and view matrix, and "M" is my model matrix. Although similar to OpenGL in C, you can see things like string functions are a little bit easier to deal with. If you refresh the browser and look in the console you should get an error message (including shader and linker logs) if that didn't work. If you want to be sure that the shader code loaded, you can use alert (vs_str); to throw up an alert box with the vertex shader string.
We can handle our asynchronous download with AJAX in the same way as we handled the texture - adding an is_loaded attribute. I'll also check that the texture was loaded, because it would look terrible if the VAO loaded first, and rendered with some other texture:
Note that, similar to the texture creation, the first step is to create a valid, but empty, VAO. This will be returned to the function caller whilst the download starts. That means that even if the download has not finished, our main programme still has a valid handle to the eventual VAO, and can check its state. The send function starts the HTTP handshaking. When the file has actually downloaded to the client the onload callback function will start - some-time after the original function call returned. It's very easy to make a mistake here when testing. On a local network the download won't have a delay. From another continent, with a large mesh, the delay could take some time - when testing asynchronous download code, check over the longest and worst connection possible. You will definitely make mistakes with the little check flags and callbacks, and have code that assumes something has downloaded without sufficiently checking.
I have a relative path to my file here. Note that you must have a closing script tag - you cannot have a single, self-closing <script /> tag, as you can with other types of HTML element. This new script block should really appear before our other block, so that the browser parses it before it is used. If you don't do this it will still work, but the browser might warn you that it has been forced to load the script less efficiently.
If you're loading your web page from your desktop AJAX may complain and refuse to load files as it violates security policy. You can put your content on a local web-server - there are many light-weight servers available. You can start Chrome with the --disable-web-security command-line flag to ignore this precaution, or you can just use Firefox, which should ignore this problem entirely and load your files.
I just include my maths library with another script block. I create my view and projection matrices with familiar-looking functions:
If you put that somewhere in your script blocks we can call it. We will also write a function to repeat that will do our drawing. This can go after your start-up code if you like:
Those are the basics of WebGL, which should be enough to get started if you've done a bit of OpenGL before.
The browser has a plethora of functions and features that you can access. You can look at user interaction with the mouse, keyboard, touch-screens for mobile devices, and even the new W3C gamepad/joystick interface. You can look at setting up more sophisticated asynchronous file streaming.
Having a set of GUI tools from HTML can not be understated. You can use all of the web's additions to charts, sliders, buttons, and most importantly you have text rendering. You can overlay these on top of the canvas if you like via CSS. You can also float the canvas over other web content, and make the background transparent.
Be sure to watch the development of the new WebGL 2.0 standard, and see if your browsers already have an early version of this running.
My favourite WebGL book so far is Diego Cantor and Brandon Jones' "WebGL Beginner's Guide", at PACKT. Mozilla Developer Network has an excellent tutorial series on getting started with WebGL.