Monday, November 15, 2010

Getting Started with WebGLU

This blog post will cover the basics of creating a WebGL page using WebGLU, here's what the final result will look like
 
A live demo is available, but first and foremost you need a browser capable of WebGL, check out the Khronos wiki for more on that.




Now that WebGLU is moderately mature I felt it's a good time for a rundown of how to actually use the library. For those who are new WebGLU is a development library that makes working with WebGL a piece of cake.

A caveat of the way WebGLU is put together is that it relies on XMLHttpRequests to load it's various parts and additional files, as such loading a page from a file:// URL is likely to cause problems due to security mechanisms in modern browsers. It's best to host the code somewhere, and if you've got Python installed on your computer this couldn't be easier, just type `python -m SimpleHTTPServer` at a command line while in the WebGLU directory and navigate your browser to localhost:8000 to check it out.

First thing's first, you need a webpage, the simplest WebGLU page you can make is:

<html><head>

<script type="text/javascript" src="path/to/webglu.js">
</script>
<script type ="application/x-javascript">
function start() {
    if (!$W.initialize()) return;
    $W.start();
};
</script>

</head><body onload="start()">
<canvas id="canvas" width="500" height="500"></canvas>
</body></html>

Unfortunately this page does absolutely nothing interesting, let's fix that. Let's create a simple colored pyramid. We're going to use the default shaders that come with WebGLU which has a vec3 attribute called 'vertex' and a vec3 attribute called 'color'. They're pretty simple but if you want to see them you can check out default.frag and default.vert. Before we can create the pyramid we need to setup WebGLU. We do this by changing start() as follows.
function start() {
    if (!$W.initialize()) return;
    $W.GL.clearColor(0.9, 0.9, 0.9, 1.0);
    $W.camera.setPosition(2, 2, 3);
};
Let's break this down.

    if (!$W.initialize()) return;
$W.initialize does just that, it loads the necessary JS files and creates the WebGL context on the canvas. If your canvas' id is not "canvas" you can pass in the id (or a canvas DOM node) to initialize to let it know what canvas to use.

    $W.GL.clearColor(0.9, 0.9, 0.9, 1.0);
$W.GL is the WebGL context itself, any call you want to make to WebGL directly you call on $W.GL. Here we're just setting the background color.

    $W.camera.setPosition(2, 2, 3);
One of the useful abstractions WebGLU provides is a simple camera you can move around the scene and point at things. If you want to implement more complex cameras or rendering transforms you can override $W.drawFn() to your heart's content.

Next we have to create the pyramid itself, let's add that to start()

function start() {
    if (!$W.initialize()) return;
    $W.GL.clearColor(0.9, 0.9, 0.9, 1.0);
    $W.camera.setPosition(2, 2, 3);

    var pyramidModel = [
        ["vertex", [0, 1, 0,   // tip 0
                   -1,-1, 1,   // fl 1
                    1,-1, 1,   // fr 2
                   -1,-1,-1,   // bl 3
                    1,-1,-1]], // br 4 
 
        ["color", [1, 1, 1,
                   1, 0, 0,
                   0, 1, 0,
                   0, 0, 1,
                   1, 0, 1 ]],

        ["wglu_elements", [0, 1, 2,
                           0, 3, 4,
                           0, 3, 1,
                           0, 2, 4 ]]
        ];

    var pyramid = $W.createObject({ type: $W.GL.TRIANGLES,
                                    data: pyramidModel });
    pyramid.setPosition(0, 1, 0);
};
Once again, let's break this down

    var pyramidModel = [
        ["vertex", [...]],
        ["color", [...]],
        ["wglu_elements", [...]]
        ];
The first two items are pretty straightforward, the array will be passed as data to the named attribute in the shader associated with the object, which in this case will be the default shader described above. WebGLU is smart enough to know that "vertex" and "color" are vec3 attributes so you don't need to specify the size or bind the arrays or anything like that. It's all automated, you just give WebGLU the data and it will get it onscreen for you.

"wglu_elements" is a special reserved name, the array here will be used fore WebGL's drawElements(). If you don't supply this then drawArrays() will be called instead.

    var pyramid = $W.createObject({ type: $W.GL.TRIANGLES,
                                    data: pyramidModel });
$W.createObject is a useful function that can take a few differently formatted objects as its argument. Here we're specifying the WebGL rendering type, since it's a WebGL constant we use $W.GL to access it. We also pass the pyramid model definition we just created. This definition can come from anywhere, you can hardcode it as we did here, load it from some file, or even generate it procedurally.

    pyramid.setPosition(0, 1, 0);
Just like the camera, WebGLU objects have easily modifiable state. For objects these include position, scale and rotation.

Now that we've created the pyramid object let's get it rendering to the screen. Add the following to our start() function.

    $W.start();
It really is as simple as that! If all went well then a static multicolored pyramid should show up on screen. In fact, since that was so easy let's make this a little more interesting. After pyramid.setPosition add the following.

    pyramid.animation._update = function() {
        this.setRotation(this.age / 60, 0, 0);
    }
Now the pyramid is spinning around. This page is a good example of how WebGLU eliminates much of the tedium involved in writing WebGL code, let's take a look at the whole thing to see just how little we had to write.
<html><head>

<script type="text/javascript" src="path/to/webglu.js">
</script>
<script type ="application/x-javascript">
function start() {
    if (!$W.initialize()) return;
    $W.GL.clearColor(0.9, 0.9, 0.9, 1.0);
    $W.camera.setPosition(2, 2, 3);

    var pyramidModel = [
        ["vertex", [0, 1, 0,   // tip 0
                   -1,-1, 1,   // fl 1
                    1,-1, 1,   // fr 2
                   -1,-1,-1,   // bl 3
                    1,-1,-1]], // br 4 
 
        ["color", [1, 1, 1,
                   1, 0, 0,
                   0, 1, 0,
                   0, 0, 1,
                   1, 0, 1 ]],

        ["wglu_elements", [0, 1, 2,
                           0, 3, 4,
                           0, 3, 1,
                           0, 2, 4 ]]
        ];

    var pyramid = $W.createObject({ type: $W.GL.TRIANGLES,
                                    data: pyramidModel });
    pyramid.setPosition(0, 1, 0);
    pyramid.animation._update = function() {
        this.setRotation(this.age / 60, 0, 0);
    }
    $W.start();
};
</script>

</head><body onload="start()">
<canvas id="canvas" width="500" height="500"></canvas>
</body></html>


It doesn't get much simpler than that, next time we'll take a look at custom materials (these are an abstraction of textures and shaders)

2 comments:

  1. webglu.complete.js:2036 SyntaxError: Parse error

    in latest WebKit nightly on OS X 10.6

    ReplyDelete
  2. @silentOpen, if you're using webglu.complete.js make sure to rebuild it with mergeModules.py first.

    ReplyDelete