WebGL Workflow and Pipeline

The next step in my journey this summer will be to do some research into WebGL and figure out how I will structure my workflow and pipeline for the creation of this app. There are a few main questions that need answering:

1. How do I compile shaders in WebGL?

This is actually quite simple. As it turns out there are just a few simple steps to take: first you create the shader, then you define it’s source with a string, and finally compile.

[code lang=”javascript”]
var newShader = gl.createShader(type);
gl.shaderSource(newShader, source);
gl.compileShader(newShader);
[/code]

The type is either VERTEX_SHADER or FRAGMENT_SHADER, and then there is another couple of useful lines that allow you to display any errors while compiling:

[code lang=”javascript”]
status = this.gl.getShaderParameter(newShader, gl.COMPILE_STATUS);
if(!status)
{
console.log(this.gl.getShaderInfoLog(shader));
}
[/code]

I also learned about the WebGL program object, which contains various compiled shaders and can be activated at anytime to change the rendering pipeline. In WebGL, creating and linking a program looks something like this:

[code lang=”javascript”]
var newProgram = this.gl.createProgram();
this.gl.attachShader(nerwProgram, vert);
this.gl.attachShader(newProgram, frag);
this.gl.linkProgram(newProgram);
[/code]

and in the same way you can easily check if it succeeded:

[code lang=”javascript”]
status = this.gl.getProgramParameter(newProgram, this.gl.LINK_STATUS);
if(!status)
{
console.log(this.gl.getProgramInfoLog(newProgram));
}
[/code]

2. How will my algorithms create geometry / what does a buffer look like in WebGL?

WebGL seems to create and display geometry using buffers, similar to OpenGL

A buffer must be created, then bound before it can be edited. You can then pass arrays to the graphics card to be used in the buffer. A seperate buffer can be used for each item or an interleaved buffer can be used to store all data in the same buffer (positions, color, etc). I will setup my code to use all separate buffers, to simplify my process and ensure that data can be separated into STATIC_DRAW and DYNAMIC_DRAW so that any animations I have can run as fast as possible. The basic buffer creation looks something like this:

[code lang=”javascript”]

 //get attribute location
var attribLoc = this.gl.getAttribLocation(program, attribute);

//create and bind buffer
var buffer = this.gl.createBuffer();
if(!buffer)
{
console.log("buffer creation failed");
return null;
}
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);

//pass data to buffer
this.gl.bufferData(this.gl.ARRAY_BUFFER, data, drawType)
this.gl.enableVertexAttribArray(attribLoc);
this.gl.vertexAttribPointer(attribLoc, perVert, this.gl.FLOAT, false, 0, 0);

return buffer;

[/code]

3. How do I pass textures tot he graphics card in Javascript?

This process is fairly straightforward, it simply involves creating an image, binding it and then passing in the image data. The general process is as follows:

[code lang=”javascript”]

//create a new texture
var tex = this.gl.createTexture();
//bind texture and push in image
this.gl.bindTexture(gl.TEXTURE_2D, tex);
this.gl.texImage2D(this.gl.TEXTURE_2D, lod, colorType, this.gl.RGBA, gl.UNSIGNED_BYTE, img);
return tex;

[/code]

The internet is filled with tutorials and expamples of WebGL sites that use pre-existing WebGL libraries but no raw WebGL code. I found a wonderful resource with some simple code examples of raw WebGL by Nick Desauliners here.

Anther useful resource was some articles on WebGL fundamentals by ‘graggman’, they can be found here.

Having answered these questions, I feel that I am in a good position to start rendering simple geometry in WebGL. I think that sticking to a 2D simulation will help keep the project in scope and not over-complicate the process.

Step 1: The Environmnet

Before I get into the actually machine learning stuff, I need to setup the environment in which I’ll be running my final simulation(s). I decided that it would be useful for me to learn some basics in webGL. I am therefore creating a simple Javascript and webGL app that will run my simulation(s) in the browser.

The page itself is pretty simple:

[code language=”html”]

<!DOCTYPE html>
<html>

<head>
<title>Machine Learning Exercise</title>

<link type="text/css" href="css/main.css" rel="stylesheet"/>

<script type="text/javascript" src="js/machLearnApp.js"></script>
<script type="text/javascript" src="js/main.js"></script>

</head>

<body onload="onLoad()">
<canvas id="canvas">
canvas is not supported in your browser, please upgrade
</canvas>
</body>

</html>

[/code]

And the css that goes along with it:

[code language=”css”]

html, body{
border:none;
padding:none;
margin:none;
}
body{
background-color:#82cdff;
background-image:url("../images/back.png");
background-repeat:repeat;
}

#canvas{
width:960px;
height:580px;
background-color:#000;
margin-left:calc(50% – 480px);
}

[/code]

I have experience drawing with the canvas element in 2D as well as some minor experience with openGL so uniting the two shouldn’t be too hard. I found a great, simple tutorial for getting started here.

I simply tied into the pages existing onload function and initialized the webGL context there:

[code language=”javascript”]

function onLoad()
{
//Initialize webGL and canvas context
canvas = document.getElementById("canvas")

gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");

if(!gl)
{
alert("could not initialize webGL context");
}
else
{
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
console.log("webGL context initialized successfully");

[/code]

after this I initialize my main app (which I’ll get into later) and create an animation/update loop for it to use.

[code language=”javascript”]

//load app
app = new MachLearnApp();
app.init();

//start main loop
window.requestAnimationFrame(loop);
}
}

function loop()
{
window.requestAnimationFrame(loop);

app.update();
app.render();
}

[/code]

As for the main app, I only set up the basic functionality so that it’s ready for me to start generating content. It has an initialization function as well as simple update and render functions. I assume that my final project will require more pseudo classes but this will do for now a base setup.

[code language=”javascript”]

function MachLearnApp() {
};

MachLearnApp.prototype.init = function()
{
gl.clearColor(0.0, 0.0, 0.0, 1.0);
}

MachLearnApp.prototype.update = function()
{
}

MachLearnApp.prototype.render = function()
{
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
}

[/code]